精华内容
参与话题
问答
  • Spring Cloud 从入门到精通

    万次阅读 多人点赞 2018-07-03 02:45:08
    Spring Cloud 是一套完整的微服务解决方案,基于 Spring Boot 框架,准确的说,它不是一个框架,而是一个大的容器,它将市面上较好的微服务框架集成进来,从而简化了开发者的代码量。 本课程由浅入深带领大家一步步...

    Spring Cloud 是一套完整的微服务解决方案,基于 Spring Boot 框架,准确的说,它不是一个框架,而是一个大的容器,它将市面上较好的微服务框架集成进来,从而简化了开发者的代码量。

    本课程由浅入深带领大家一步步攻克 Spring Cloud 各大模块,接着通过一个实例带领大家了解大型分布式微服务架构的搭建过程,最后深入源码加深对它的了解。

    本课程共分为四个部分:

    第一部分(第1-3课),初识 Spring Boot,掌握 Spring Boot 基础知识,为后续入门 Spring Cloud 打好基础 。

    第二部分(第4-13课),Spring Cloud 入门篇,主要介绍 Spring Cloud 常用模块,包括服务发现、服务注册、配置中心、链路追踪、异常处理等。

    第三部分(第14-18课),Spring Cloud 进阶篇,介绍大型分布式系统中事务处理、线程安全等问题,并以一个实例项目手把手教大家搭建完整的微服务系统。

    第四部分(第19-20课),Spring Cloud 高级篇,解析 Spring Cloud 源码,并讲解如何部署基于 Spring Cloud 的大型分布式系统。

    点击购买,可阅读课程全文

    作者介绍

    李熠,从事 Java 后端开发 6 年,现任职某大型互联网公司,担任 Java 高级开发工程师,CSDN 博客专家,全栈工程师。

    课程内容

    导读:什么是 Spring Cloud 及应用现状

    Spring Cloud 是什么?

    在学习本课程之前,读者有必要先了解一下 Spring Cloud。

    Spring Cloud 是一系列框架的有序集合,它利用 Spring Boot 的开发便利性简化了分布式系统的开发,比如服务发现、服务网关、服务路由、链路追踪等。Spring Cloud 并不重复造轮子,而是将市面上开发得比较好的模块集成进去,进行封装,从而减少了各模块的开发成本。换句话说:Spring Cloud 提供了构建分布式系统所需的“全家桶”。

    Spring Cloud 现状

    目前,国内使用 Spring Cloud 技术的公司并不多见,不是因为 Spring Cloud 不好,主要原因有以下几点:

    1. Spring Cloud 中文文档较少,出现问题网上没有太多的解决方案。
    2. 国内创业型公司技术老大大多是阿里系员工,而阿里系多采用 Dubbo 来构建微服务架构。
    3. 大型公司基本都有自己的分布式解决方案,而中小型公司的架构很多用不上微服务,所以没有采用 Spring Cloud 的必要性。

    但是,微服务架构是一个趋势,而 Spring Cloud 是微服务解决方案的佼佼者,这也是作者写本系列课程的意义所在。

    Spring Cloud 优缺点

    其主要优点有:

    1. 集大成者,Spring Cloud 包含了微服务架构的方方面面。
    2. 约定优于配置,基于注解,没有配置文件。
    3. 轻量级组件,Spring Cloud 整合的组件大多比较轻量级,且都是各自领域的佼佼者。
    4. 开发简便,Spring Cloud 对各个组件进行了大量的封装,从而简化了开发。
    5. 开发灵活,Spring Cloud 的组件都是解耦的,开发人员可以灵活按需选择组件。

    接下来,我们看下它的缺点:

    1. 项目结构复杂,每一个组件或者每一个服务都需要创建一个项目。
    2. 部署门槛高,项目部署需要配合 Docker 等容器技术进行集群部署,而要想深入了解 Docker,学习成本高。

    Spring Cloud 的优势是显而易见的。因此对于想研究微服务架构的同学来说,学习 Spring Cloud 是一个不错的选择。

    Spring Cloud 和 Dubbo 对比

    Dubbo 只是实现了服务治理,而 Spring Cloud 实现了微服务架构的方方面面,服务治理只是其中的一个方面。下面通过一张图对其进行比较:

    这里写图片描述

    (图片引自:程序猿DD,作者:翟永超)

    可以看出,Spring Cloud 比较全面,而 Dubbo 由于只实现了服务治理,需要集成其他模块,需要单独引入,增加了学习成本和集成成本。

    Spring Cloud 学习

    Spring Cloud 基于 Spring Boot,因此在研究 Spring Cloud 之前,本课程会首先介绍 Spring Boot 的用法,方便后续 Spring Cloud 的学习。

    本课程不会讲解 Spring MVC 的用法,因此学习本课程需要读者对 Spring 及 Spring MVC 有过研究。

    本课程共分为四个部分:

    • 第一部分初识 Spring Boot,掌握 Spring Boot 基础知识,为后续入门 Spring Cloud 打好基础 。

    • 第二部分 Spring Cloud 入门篇,主要介绍 Spring Cloud 常用模块,包括服务发现、服务注册、配置中心、链路追踪、异常处理等。

    • 第三部分 Spring Cloud 进阶篇,介绍大型分布式系统中事务处理、线程安全等问题,并以一个实例项目手把手教大家搭建完整的微服务系统。

    • 第四部分 Spring Cloud 高级篇,解析 Spring Cloud 源码,并讲解如何部署基于 Spring Cloud 的大型分布式系统。

    本课程的所有示例代码均可在:https://github.com/lynnlovemin/SpringCloudLesson 下载。

    第01课:Spring Boot 入门

    什么是 Spring Boot

    Spring Boot 是由 Pivotal 团队提供的基于 Spring 的全新框架,其设计目的是为了简化 Spring 应用的搭建和开发过程。该框架遵循“约定大于配置”原则,采用特定的方式进行配置,从而使开发者无需定义大量的 XML 配置。通过这种方式,Spring Boot 致力于在蓬勃发展的快速应用开发领域成为领导者。

    Spring Boot 并不重复造轮子,而且在原有 Spring 的框架基础上封装了一层,并且它集成了一些类库,用于简化开发。换句话说,Spring Boot 就是一个大容器。

    下面几张图展示了官网上提供的 Spring Boot 所集成的所有类库:

    这里写图片描述

    这里写图片描述

    这里写图片描述

    Spring Boot 官方推荐使用 Maven 或 Gradle 来构建项目,本教程采用 Maven。

    第一个 Spring Boot 项目

    大多数教程都是以 Hello World 入门,本教程也不例外,接下来,我们就来搭建一个最简单的 Spring Boot 项目。

    1.创建一个 Maven 工程,请看下图:

    这里写图片描述

    2.在 pom.xml 加入 Spring Boot 依赖:

    <parent>    <groupId>org.springframework.boot</groupId>    <artifactId>spring-boot-starter-parent</artifactId>    <version>2.1.3.RELEASE</version></parent><dependencies>    <dependency>        <groupId>org.springframework.boot</groupId>        <artifactId>spring-boot-starter-web</artifactId>    </dependency></dependencies>

    3.创建应用程序启动类 DemoApplication,并编写以下代码:

    import org.springframework.boot.SpringApplication;import org.springframework.boot.autoconfigure.SpringBootApplication;@SpringBootApplicationpublic class DemoApplication {    public static void main(String[] args) {        SpringApplication.run(DemoApplication.class, args);    }}

    4.创建一个 Controller 类 HelloController,用以测试我们的第一个基于 Spring Boot 的 Web 应用:

    import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.RestController;@RestControllerpublic class HelloController {    @RequestMapping("hello")    String hello() {        return "Hello World!";    }}

    5.运行 DemoApplication 类中的 main 方法,看到下图所示内容说明应用启动成功:

    enter image description here

    6.浏览器访问:http://localhost:8080/hello,则会看到下图所示界面:

    这里写图片描述

    我们可以注意到,没有写任何的配置文件,更没有显示使用任何容器,只需要启动 Main 方法即可开启 Web 服务,从而访问到 HelloController 类里定义的路由地址。

    这就是 Spring Boot 的强大之处,它默认集成了 Tomcat 容器,通过 Main 方法编写的 SpringApplication.run 方法即可启动内置 Tomcat。

    它是如何启动的,内部又是如何运行的呢?具体原理我将在《第03课:Spring Boot 启动原理》一节中具体分析。

    在上面的示例中,我们没有定义应用程序启动端口,可以看到控制台,它开启了 8080 端口,这是 Spring Boot 默认的启动端口。Spring Boot 提供了默认的配置,我们也可以改变这些配置,具体方法将在后面介绍。

    我们在启动类里加入 @SpringBootApplication 注解,则这个类就是整个应用程序的启动类。如果不加这个注解,启动程序将会报错,读者可以尝试一下。

    Spring Boot 还有一个特点是利用注解代码繁琐的 XML 配置,整个应用程序只有一个入口配置文件,那就是 application.yml 或 application.properties。接下来,我将介绍其配置文件的用法。

    properties 和 yaml

    在前面的示例代码中,我们并没有看到该配置文件,那是因为 Spring Boot 对每个配置项都有默认值。当然,我们也可以添加配置文件,用以覆盖其默认值,这里以 .properties 文件为例,首先在 resources 下新建一个名为 application.properties(注意:文件名必须是 application)的文件,键入内容为:

    server.port=8081server.servlet.context-path=/api

    并且启动 Main 方法,这时程序请求地址则变成了:http://localhost:8081/api/hello。

    Spring Boot 支持 properties 和 yaml 两种格式的文件,文件名分别对应 application.properties 和 application.yml,下面贴出 yaml 文件格式供大家参考:

    server:  port: 8080  servlet:    context-path: /api

    可以看出 properties 是以逗号隔开,而 yaml 则换行+ 两个空格 隔开,这里需要注意的是冒号后面必须空格,否则会报错。yaml 文件格式更清晰,更易读,这里作者建议大家都采用 yaml 文件来配置。

    以上示例只是小试牛刀,更多的配置将在后面的课程中讲解。

    本教程的所有配置均采用 yaml 文件。

    打包、运行

    Spring Boot 打包分为 war 和 jar 两个格式,下面将分别演示如何构建这两种格式的启动包。

    在 pom.xml 加入如下配置:

    <packaging>war</packaging><build>    <finalName>api</finalName>    <resources>        <resource>            <directory>src/main/resources</directory>            <filtering>true</filtering>        </resource>    </resources>    <plugins>        <plugin>            <groupId>org.springframework.boot</groupId>            <artifactId>spring-boot-maven-plugin</artifactId>        </plugin>        <plugin>            <artifactId>maven-resources-plugin</artifactId>            <version>2.5</version>            <configuration>                <encoding>UTF-8</encoding>            </configuration>        </plugin>        <plugin>            <groupId>org.apache.maven.plugins</groupId>            <artifactId>maven-surefire-plugin</artifactId>            <version>2.18.1</version>            <configuration>                <skipTests>true</skipTests>            </configuration>        </plugin>    </plugins></build>

    这个时候运行 mvn package 就会生成 war 包,然后放到 Tomcat 当中就能启动,但是我们单纯这样配置在 Tomcat 是不能成功运行的,会报错,需要通过编码指定 Tomcat 容器启动,修改 DemoApplication 类:

    import org.springframework.boot.SpringApplication;import org.springframework.boot.autoconfigure.SpringBootApplication;import org.springframework.boot.builder.SpringApplicationBuilder;import org.springframework.boot.web.servlet.support.SpringBootServletInitializer;@SpringBootApplicationpublic class DemoApplication extends SpringBootServletInitializer {    public static void main(String[] args) {        SpringApplication.run(DemoApplication.class, args);    }    @Override    protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {        return application.sources(DemoApplication.class);    }}

    这时再打包放到 Tomcat,启动就不会报错了。

    在上述代码中,DemoApplication 类继承了 SpringBootServletInitializer,并重写 configure 方法,目的是告诉外部 Tomcat,启动时执行该方法,然后在该方法体内指定应用程序入口为 DemoApplication 类,如果通过外部 Tomcat 启动 Spring Boot 应用,则其配置文件设置的端口和 contextPath 是无效的。这时,应用程序的启动端口即是 Tomcat 的启动端口,contextPath 和 war 包的文件名相同。

    接下来我们继续看如果达成 jar 包,在 pom.xml 加入如下配置:

    <!-- 需要将包类型改为 jar 包 --><packaging>jar</packaging><build>    <finalName>api</finalName>    <resources>        <resource>            <directory>src/main/resources</directory>            <filtering>true</filtering>        </resource>    </resources>    <plugins>        <plugin>            <groupId>org.springframework.boot</groupId>            <artifactId>spring-boot-maven-plugin</artifactId>            <configuration>                <fork>true</fork>                <!-- 指定 Main 方法所在类 -->                <mainClass>com.lynn.DemoApplication</mainClass>            </configuration>            <executions>                <execution>                    <goals>                        <goal>repackage</goal>                    </goals>                </execution>            </executions>        </plugin>        <plugin>            <artifactId>maven-resources-plugin</artifactId>            <version>2.5</version>            <configuration>                <encoding>UTF-8</encoding>                <useDefaultDelimiters>true</useDefaultDelimiters>            </configuration>        </plugin>        <plugin>            <groupId>org.apache.maven.plugins</groupId>            <artifactId>maven-surefire-plugin</artifactId>            <version>2.18.1</version>            <configuration>                <skipTests>true</skipTests>            </configuration>        </plugin>        <plugin>            <groupId>org.apache.maven.plugins</groupId>            <artifactId>maven-compiler-plugin</artifactId>            <version>2.3.2</version>            <configuration>                <source>1.8</source>                <target>1.8</target>            </configuration>        </plugin>    </plugins></build>

    然后通过 mvn package 打包,最后通过 java 命令启动:

    java -jar api.jar

    如果是 Linux 服务器,上述命令是前台进程,点击 Ctrl+C 进程就会停止,可以考虑用 nohup 命令开启守护进程,这样应用程序才不会自动停止。

    这样,最简单的 Spring Boot 就完成了,但是对于一个大型项目,这是远远不够的,Spring Boot 的详细操作可以参照官网

    下面展示一个最基础的企业级 Spring Boot 项目的结构:

    这里写图片描述

    其中,Application.java 是程序的启动类,Startup.java 是程序启动完成前执行的类,WebConfig.java 是配置类,所有 Bean 注入、配置、拦截器注入等都放在这个类里面。

    以上实例只是最简单的 Spring Boot 项目入门实例,后面会深入研究 Spring Boot。

    第02课:Spring Boot 进阶

    上一篇带领大家初步了解了如何使用 Spring Boot 搭建框架,通过 Spring Boot 和传统的 SpringMVC 架构的对比,我们清晰地发现 Spring Boot 的好处,它使我们的代码更加简单,结构更加清晰。

    从这一篇开始,我将带领大家更加深入的认识 Spring Boot,将 Spring Boot 涉及到东西进行拆解,从而了解 Spring Boot 的方方面面。学完本文后,读者可以基于 Spring Boot 搭建更加复杂的系统框架。

    我们知道,Spring Boot 是一个大容器,它将很多第三方框架都进行了集成,我们在实际项目中用到哪个模块,再引入哪个模块。比如我们项目中的持久化框架用 MyBatis,则在 pom.xml 添加如下依赖:

    <dependency>            <groupId>org.mybatis.spring.boot</groupId>            <artifactId>mybatis-spring-boot-starter</artifactId>            <version>1.1.1</version>        </dependency>        <dependency>            <groupId>mysql</groupId>            <artifactId>mysql-connector-java</artifactId>            <version>5.1.40</version>        </dependency>

    yaml/properties 文件

    我们知道整个 Spring Boot 项目只有一个配置文件,那就是 application.yml,Spring Boot 在启动时,就会从 application.yml 中读取配置信息,并加载到内存中。上一篇我们只是粗略的列举了几个配置项,其实 Spring Boot 的配置项是很多的,本文我们将学习在实际项目中常用的配置项(注:为了方便说明,配置项均以 properties 文件的格式写出,后续的实际配置都会写成 yaml 格式)。

    配置项 说明 举例
    server.port 应用程序启动端口 server.port=8080,定义应用程序启动端口为 8080
    server.servlet.context-path 应用程序上下文 server.servlet.context-path=/api,则访问地址为:http://ip:port/api
    spring.servlet.multipart.maxFileSize 最大文件上传大小,-1为不限制 spring.servlet.multipart.maxFileSize=-1
    spring.jpa.database 数据库类型 spring.jpa.database=MYSQL,指定数据库为mysql
    spring.jpa.properties.hibernate.dialect hql方言 spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL5Dialect
    spring.datasource.url 数据库连接字符串 spring.datasource.url=jdbc:mysql://localhost:3306/database?useUnicode=true&characterEncoding=UTF-8&useSSL=true
    spring.datasource.username 数据库用户名 spring.datasource.username=root
    spring.datasource.password 数据库密码 spring.datasource.password=root
    spring.datasource.driverClassName 数据库驱动 spring.datasource.driverClassName=com.mysql.jdbc.Driver
    spring.jpa.showSql 控制台是否打印 SQL 语句 spring.jpa.showSql=true

    下面是我参与的某个项目的 application.yml 配置文件内容:

    server:  port: 8080  servlet:    context-path: /api  tomcat:    basedir: /data/tmp    max-threads: 1000    min-spare-threads: 50  connection-timeout: 5000spring:  profiles:    active: dev  servlet:    multipart:      maxFileSize: -1  datasource:    url: jdbc:mysql://localhost:3306/database?useUnicode=true&characterEncoding=UTF-8&useSSL=true    username: root    password: root    driverClassName: com.mysql.jdbc.Driver  jpa:    database: MYSQL    showSql: true    hibernate:      namingStrategy: org.hibernate.cfg.ImprovedNamingStrategy    properties:      hibernate:        dialect: org.hibernate.dialect.MySQL5Dialectmybatis:  configuration:     #配置项:开启下划线到驼峰的自动转换. 作用:将数据库字段根据驼峰规则自动注入到对象属性     map-underscore-to-camel-case: true

    以上列举了常用的配置项,所有配置项信息都可以在官网中找到,本课程就不一一列举了。

    多环境配置

    在一个企业级系统中,我们可能会遇到这样一个问题:开发时使用开发环境,测试时使用测试环境,上线时使用生产环境。每个环境的配置都可能不一样,比如开发环境的数据库是本地地址,而测试环境的数据库是测试地址。那我们在打包的时候如何生成不同环境的包呢?

    这里的解决方案有很多:

    1. 每次编译之前手动把所有配置信息修改成当前运行的环境信息。这种方式导致每次都需要修改,相当麻烦,也容易出错。
    2. 利用 Maven,在 pom.xml 里配置多个环境,每次编译之前将 settings.xml 里面修改成当前要编译的环境 ID。这种方式会事先设置好所有环境,缺点就是每次也需要手动指定环境,如果环境指定错误,发布时是不知道的。
    3. 第三种方案就是本文重点介绍的,也是作者强烈推荐的方式。

    首先,创建 application.yml 文件,在里面添加如下内容:

    spring:  profiles:    active: dev

    含义是指定当前项目的默认环境为 dev,即项目启动时如果不指定任何环境,Spring Boot 会自动从 dev 环境文件中读取配置信息。我们可以将不同环境都共同的配置信息写到这个文件中。

    然后创建多环境配置文件,文件名的格式为:application-{profile}.yml,其中,{profile} 替换为环境名字,如 application-dev.yml,我们可以在其中添加当前环境的配置信息,如添加数据源:

    spring:  datasource:    url: jdbc:mysql://localhost:3306/database?useUnicode=true&characterEncoding=UTF-8&useSSL=true    username: root    password: root    driverClassName: com.mysql.jdbc.Driver

    这样,我们就实现了多环境的配置,每次编译打包我们无需修改任何东西,编译为 jar 文件后,运行命令:

    java -jar api.jar --spring.profiles.active=dev

    其中 --spring.profiles.active 就是我们要指定的环境。

    常用注解

    我们知道,Spring Boot 主要采用注解的方式,在《第01课:Spring Boot 入门》一节的入门实例中,我们也用到了一些注解。

    本文,我将详细介绍在实际项目中常用的注解。

    @SpringBootApplication

    我们可以注意到 Spring Boot 支持 main 方法启动,在我们需要启动的主类中加入此注解,告诉 Spring Boot,这个类是程序的入口。如:

    @SpringBootApplicationpublic class DemoApplication {    public static void main(String[] args) {        SpringApplication.run(DemoApplication.class, args);    }}

    如果不加这个注解,程序是无法启动的。

    我们查看下 SpringBootApplication 的源码,源码如下:

    @Target(ElementType.TYPE)@Retention(RetentionPolicy.RUNTIME)@Documented@Inherited@SpringBootConfiguration@EnableAutoConfiguration@ComponentScan(excludeFilters = {        @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),        @Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })public @interface SpringBootApplication {    /**     * Exclude specific auto-configuration classes such that they will never be applied.     * @return the classes to exclude     */    @AliasFor(annotation = EnableAutoConfiguration.class, attribute = "exclude")    Class<?>[] exclude() default {};    /**     * Exclude specific auto-configuration class names such that they will never be     * applied.     * @return the class names to exclude     * @since 1.3.0     */    @AliasFor(annotation = EnableAutoConfiguration.class, attribute = "excludeName")    String[] excludeName() default {};    /**     * Base packages to scan for annotated components. Use {@link #scanBasePackageClasses}     * for a type-safe alternative to String-based package names.     * @return base packages to scan     * @since 1.3.0     */    @AliasFor(annotation = ComponentScan.class, attribute = "basePackages")    String[] scanBasePackages() default {};    /**     * Type-safe alternative to {@link #scanBasePackages} for specifying the packages to     * scan for annotated components. The package of each class specified will be scanned.     * <p>     * Consider creating a special no-op marker class or interface in each package that     * serves no purpose other than being referenced by this attribute.     * @return base packages to scan     * @since 1.3.0     */    @AliasFor(annotation = ComponentScan.class, attribute = "basePackageClasses")    Class<?>[] scanBasePackageClasses() default {};}

    在这个注解类上有 3 个注解,如下:

    @SpringBootConfiguration@EnableAutoConfiguration@ComponentScan(excludeFilters = {        @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),        @Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })

    因此,我们可以用这三个注解代替 SpringBootApplication,如:

    @SpringBootConfiguration@EnableAutoConfiguration@ComponentScanpublic class Application {    public static void main(String[] args) {        SpringApplication.run(Application.class, args);    }}

    其中,SpringBootConfiguration 表示 Spring Boot 的配置注解,EnableAutoConfiguration 表示自动配置,ComponentScan 表示 Spring Boot 扫描 Bean 的规则,比如扫描哪些包。

    @Configuration

    加入了这个注解的类被认为是 Spring Boot 的配置类,我们知道可以在 application.yml 设置一些配置,也可以通过代码设置配置。

    如果我们要通过代码设置配置,就必须在这个类上标注 Configuration 注解。如下代码:

    @Configurationpublic class WebConfig extends WebMvcConfigurationSupport{    @Override    protected void addInterceptors(InterceptorRegistry registry) {        super.addInterceptors(registry);        registry.addInterceptor(new ApiInterceptor());    }}

    不过 Spring Boot 官方推荐 Spring Boot 项目用 SpringBootConfiguration 来代替 Configuration。

    @Bean

    这个注解是方法级别上的注解,主要添加在 @Configuration@SpringBootConfiguration 注解的类,有时也可以添加在 @Component 注解的类。它的作用是定义一个Bean。

    请看下面代码:

        @Bean    public ApiInterceptor interceptor(){        return new ApiInterceptor();    }

    那么,我们可以在 ApiInterceptor 里面注入其他 Bean,也可以在其他 Bean 注入这个类。

    @Value

    通常情况下,我们需要定义一些全局变量,都会想到的方法是定义一个 public static 变量,在需要时调用,是否有其他更好的方案呢?答案是肯定的。下面请看代码:

        @Value("${server.port}")    String port;    @RequestMapping("/hello")    public String home(String name) {        return "hi "+name+",i am from port:" +port;    }

    其中,server.port 就是我们在 application.yml 里面定义的属性,我们可以自定义任意属性名,通过 @Value 注解就可以将其取出来。

    它的好处不言而喻:

    1. 定义在配置文件里,变量发生变化,无需修改代码。
    2. 变量交给Spring来管理,性能更好。

    注: 本课程默认针对于对 SpringMVC 有所了解的读者,Spring Boot 本身基于 Spring 开发的,因此,本文不再讲解其他 Spring 的注解。

    注入任何类

    本节通过一个实际的例子来讲解如何注入一个普通类,并且说明这样做的好处。

    假设一个需求是这样的:项目要求使用阿里云的 OSS 进行文件上传。

    我们知道,一个项目一般会分为开发环境、测试环境和生产环境。OSS 文件上传一般有如下几个参数:appKey、appSecret、bucket、endpoint 等。不同环境的参数都可能不一样,这样便于区分。按照传统的做法,我们在代码里设置这些参数,这样做的话,每次发布不同的环境包都需要手动修改代码。

    这个时候,我们就可以考虑将这些参数定义到配置文件里面,通过前面提到的 @Value 注解取出来,再通过 @Bean 将其定义为一个 Bean,这时我们只需要在需要使用的地方注入该 Bean 即可。

    首先在 application.yml 加入如下内容:

    oss:  appKey: 1  appSecret: 1  bucket: lynn  endPoint: https://www.aliyun.com

    其次创建一个普通类:

    public class Aliyun {    private String appKey;    private String appSecret;    private String bucket;    private String endPoint;    public static class Builder{        private String appKey;        private String appSecret;        private String bucket;        private String endPoint;        public Builder setAppKey(String appKey){            this.appKey = appKey;            return this;        }        public Builder setAppSecret(String appSecret){            this.appSecret = appSecret;            return this;        }        public Builder setBucket(String bucket){            this.bucket = bucket;            return this;        }        public Builder setEndPoint(String endPoint){            this.endPoint = endPoint;            return this;        }        public Aliyun build(){            return new Aliyun(this);        }    }    public static Builder options(){        return new Aliyun.Builder();    }    private Aliyun(Builder builder){        this.appKey = builder.appKey;        this.appSecret = builder.appSecret;        this.bucket = builder.bucket;        this.endPoint = builder.endPoint;    }    public String getAppKey() {        return appKey;    }    public String getAppSecret() {        return appSecret;    }    public String getBucket() {        return bucket;    }    public String getEndPoint() {        return endPoint;    }}

    然后在 @SpringBootConfiguration 注解的类添加如下代码:

        @Value("${oss.appKey}")    private String appKey;    @Value("${oss.appSecret}")    private String appSecret;    @Value("${oss.bucket}")    private String bucket;    @Value("${oss.endPoint}")    private String endPoint;    @Bean    public Aliyun aliyun(){        return Aliyun.options()                .setAppKey(appKey)                .setAppSecret(appSecret)                .setBucket(bucket)                .setEndPoint(endPoint)                .build();    }

    最后在需要的地方注入这个 Bean 即可:

        @Autowired    private Aliyun aliyun;

    以上代码其实并不完美,如果增加一个属性,就需要在 Aliyun 类新增一个字段,还需要在 Configuration 类里注入它,扩展性不好,那么我们有没有更好的方式呢?

    答案是肯定的,我们可以利用 ConfigurationProperties 注解更轻松地将配置信息注入到实体中。

    1.创建实体类 AliyunAuto,并编写以下代码:

    import org.springframework.boot.context.properties.ConfigurationProperties;import org.springframework.stereotype.Component;@Component@ConfigurationProperties(prefix = "oss")public class AliyunAuto {    private String appKey;    private String appSecret;    private String bucket;    private String endPoint;    public String getAppKey() {        return appKey;    }    public void setAppKey(String appKey) {        this.appKey = appKey;    }    public String getAppSecret() {        return appSecret;    }    public void setAppSecret(String appSecret) {        this.appSecret = appSecret;    }    public String getBucket() {        return bucket;    }    public void setBucket(String bucket) {        this.bucket = bucket;    }    public String getEndPoint() {        return endPoint;    }    public void setEndPoint(String endPoint) {        this.endPoint = endPoint;    }    @Override    public String toString() {        return "AliyunAuto{" +                "appKey='" + appKey + '\'' +                ", appSecret='" + appSecret + '\'' +                ", bucket='" + bucket + '\'' +                ", endPoint='" + endPoint + '\'' +                '}';    }}

    其中,ConfigurationProperties 指定配置文件的前缀属性,实体具体字段和配置文件字段名一致,如在上述代码中,字段为 appKey,则自动获取 oss.appKey 的值,将其映射到 appKey 字段中,这样就完成了自动的注入。如果我们增加一个属性,则只需要修改 Bean 和配置文件即可,不用显示注入。

    拦截器

    我们在提供 API 的时候,经常需要对 API 进行统一的拦截,比如进行接口的安全性校验。

    本节,我会讲解 Spring Boot 是如何进行拦截器设置的,请看接下来的代码。

    创建一个拦截器类:ApiInterceptor,并实现 HandlerInterceptor 接口:

    public class ApiInterceptor implements HandlerInterceptor {    //请求之前    @Override    public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o) throws Exception {        System.out.println("进入拦截器");        return true;    }    //请求时    @Override    public void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, ModelAndView modelAndView) throws Exception {    }    //请求完成    @Override    public void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) throws Exception {    }}

    @SpringBootConfiguration 注解的类继承 WebMvcConfigurationSupport 类,并重写 addInterceptors 方法,将 ApiInterceptor 拦截器类添加进去,代码如下:

    @SpringBootConfigurationpublic class WebConfig extends WebMvcConfigurationSupport{    @Override    protected void addInterceptors(InterceptorRegistry registry) {        super.addInterceptors(registry);        registry.addInterceptor(new ApiInterceptor());    }}

    异常处理

    我们在 Controller 里提供接口,通常需要捕捉异常,并进行友好提示,否则一旦出错,界面上就会显示报错信息,给用户一种不好的体验。最简单的做法就是每个方法都使用 try catch 进行捕捉,报错后,则在 catch 里面设置友好的报错提示。如果方法很多,每个都需要 try catch,代码会显得臃肿,写起来也比较麻烦。

    我们可不可以提供一个公共的入口进行统一的异常处理呢?当然可以。方法很多,这里我们通过 Spring 的 AOP 特性就可以很方便的实现异常的统一处理。实现方法很简单,只需要在 Controller 类添加以下代码即可。

        @ExceptionHandler    public String doError(Exception ex) throws Exception{        ex.printStackTrace();        return ex.getMessage();    }

    其中,在 doError 方法上加入 @ExceptionHandler 注解即可,这样,接口发生异常会自动调用该方法。

    这样,我们无需每个方法都添加 try catch,一旦报错,则会执行 handleThrowing 方法。

    优雅的输入合法性校验

    为了接口的健壮性,我们通常除了客户端进行输入合法性校验外,在 Controller 的方法里,我们也需要对参数进行合法性校验,传统的做法是每个方法的参数都做一遍判断,这种方式和上一节讲的异常处理一个道理,不太优雅,也不易维护。

    其实,SpringMVC 提供了验证接口,下面请看代码:

    @GetMapping("authorize")public void authorize(@Valid AuthorizeIn authorize, BindingResult ret){    if(result.hasFieldErrors()){            List<FieldError> errorList = result.getFieldErrors();            //通过断言抛出参数不合法的异常            errorList.stream().forEach(item -> Assert.isTrue(false,item.getDefaultMessage()));        }}public class AuthorizeIn extends BaseModel{    @NotBlank(message = "缺少response_type参数")    private String responseType;    @NotBlank(message = "缺少client_id参数")    private String ClientId;    private String state;    @NotBlank(message = "缺少redirect_uri参数")    private String redirectUri;    public String getResponseType() {        return responseType;    }    public void setResponseType(String responseType) {        this.responseType = responseType;    }    public String getClientId() {        return ClientId;    }    public void setClientId(String clientId) {        ClientId = clientId;    }    public String getState() {        return state;    }    public void setState(String state) {        this.state = state;    }    public String getRedirectUri() {        return redirectUri;    }    public void setRedirectUri(String redirectUri) {        this.redirectUri = redirectUri;    }}

    我们再把验证的代码单独封装成方法:

    protected void validate(BindingResult result){        if(result.hasFieldErrors()){            List<FieldError> errorList = result.getFieldErrors();            errorList.stream().forEach(item -> Assert.isTrue(false,item.getDefaultMessage()));        }    }

    这样每次参数校验只需要调用 validate 方法就行了,我们可以看到代码的可读性也大大提高了。

    接口版本控制

    一个系统上线后会不断迭代更新,需求也会不断变化,有可能接口的参数也会发生变化,如果在原有的参数上直接修改,可能会影响线上系统的正常运行,这时我们就需要设置不同的版本,这样即使参数发生变化,由于老版本没有变化,因此不会影响上线系统的运行。

    一般我们可以在地址上带上版本号,也可以在参数上带上版本号,还可以再 header 里带上版本号,这里我们在地址上带上版本号,大致的地址如:http://api.example.com/v1/test,其中,v1 即代表的是版本号。具体做法请看代码:

    @Target({ElementType.METHOD,ElementType.TYPE})@Retention(RetentionPolicy.RUNTIME)@Documented@Mappingpublic @interface ApiVersion {    /**     * 标识版本号     * @return     */    int value();}public class ApiVersionCondition implements RequestCondition<ApiVersionCondition> {    // 路径中版本的前缀, 这里用 /v[1-9]/的形式    private final static Pattern VERSION_PREFIX_PATTERN = Pattern.compile("v(\\d+)/");    private int apiVersion;    public ApiVersionCondition(int apiVersion){        this.apiVersion = apiVersion;    }    @Override    public ApiVersionCondition combine(ApiVersionCondition other) {        // 采用最后定义优先原则,则方法上的定义覆盖类上面的定义        return new ApiVersionCondition(other.getApiVersion());    }    @Override    public ApiVersionCondition getMatchingCondition(HttpServletRequest request) {        Matcher m = VERSION_PREFIX_PATTERN.matcher(request.getRequestURI());        if(m.find()){            Integer version = Integer.valueOf(m.group(1));            if(version >= this.apiVersion)            {                return this;            }        }        return null;    }    @Override    public int compareTo(ApiVersionCondition other, HttpServletRequest request) {        // 优先匹配最新的版本号        return other.getApiVersion() - this.apiVersion;    }    public int getApiVersion() {        return apiVersion;    }}public class CustomRequestMappingHandlerMapping extends        RequestMappingHandlerMapping {    @Override    protected RequestCondition<ApiVersionCondition> getCustomTypeCondition(Class<?> handlerType) {        ApiVersion apiVersion = AnnotationUtils.findAnnotation(handlerType, ApiVersion.class);        return createCondition(apiVersion);    }    @Override    protected RequestCondition<ApiVersionCondition> getCustomMethodCondition(Method method) {        ApiVersion apiVersion = AnnotationUtils.findAnnotation(method, ApiVersion.class);        return createCondition(apiVersion);    }    private RequestCondition<ApiVersionCondition> createCondition(ApiVersion apiVersion) {        return apiVersion == null ? null : new ApiVersionCondition(apiVersion.value());    }}@SpringBootConfigurationpublic class WebConfig extends WebMvcConfigurationSupport {    @Bean    public AuthInterceptor interceptor(){        return new AuthInterceptor();    }    @Override    public void addInterceptors(InterceptorRegistry registry) {        registry.addInterceptor(new AuthInterceptor());    }    @Override    @Bean    public RequestMappingHandlerMapping requestMappingHandlerMapping() {        RequestMappingHandlerMapping handlerMapping = new CustomRequestMappingHandlerMapping();        handlerMapping.setOrder(0);        handlerMapping.setInterceptors(getInterceptors());        return handlerMapping;    }}

    Controller 类的接口定义如下:

    @ApiVersion(1)@RequestMapping("{version}/dd")public class HelloController{}

    这样我们就实现了版本控制,如果增加了一个版本,则创建一个新的 Controller,方法名一致,ApiVersion 设置为2,则地址中 v1 会找到 ApiVersion 为1的方法,v2 会找到 ApiVersion 为2的方法。

    自定义 JSON 解析

    Spring Boot 中 RestController 返回的字符串默认使用 Jackson 引擎,它也提供了工厂类,我们可以自定义 JSON 引擎,本节实例我们将 JSON 引擎替换为 fastJSON,首先需要引入 fastJSON:

    <dependency>            <groupId>com.alibaba</groupId>            <artifactId>fastjson</artifactId>            <version>${fastjson.version}</version>        </dependency>

    其次,在 WebConfig 类重写 configureMessageConverters 方法:

    @Override    public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {        super.configureMessageConverters(converters);        /*        1.需要先定义一个 convert 转换消息的对象;        2.添加 fastjson 的配置信息,比如是否要格式化返回的 JSON 数据        3.在 convert 中添加配置信息        4.将 convert 添加到 converters 中         */        //1.定义一个 convert 转换消息对象        FastJsonHttpMessageConverter fastConverter=new FastJsonHttpMessageConverter();        //2.添加 fastjson 的配置信息,比如是否要格式化返回 JSON 数据        FastJsonConfig fastJsonConfig=new FastJsonConfig();        fastJsonConfig.setSerializerFeatures(                SerializerFeature.PrettyFormat        );        fastConverter.setFastJsonConfig(fastJsonConfig);        converters.add(fastConverter);    }

    单元测试

    Spring Boot 的单元测试很简单,直接看代码:

    @SpringBootTest(classes = DemoApplication.class)@RunWith(SpringJUnit4ClassRunner.class)public class TestDB {    @Test    public void test(){    }}

    模板引擎

    在传统的 SpringMVC 架构中,我们一般将 JSP、HTML 页面放到 webapps 目录下面,但是 Spring Boot 没有 webapps,更没有 web.xml,如果我们要写界面的话,该如何做呢?

    Spring Boot 官方提供了几种模板引擎:FreeMarker、Velocity、Thymeleaf、Groovy、mustache、JSP。

    这里以 FreeMarker 为例讲解 Spring Boot 的使用。

    首先引入 FreeMarker 依赖:

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

    在 resources 下面建立两个目录:static 和 templates,如图所示:

    这里写图片描述

    其中 static 目录用于存放静态资源,譬如 CSS、JS、HTML 等,templates 目录存放模板引擎文件,我们可以在 templates 下面创建一个文件:index.ftl(freemarker 默认后缀为 .ftl),并添加内容:

    <!DOCTYPE html><html>    <head>    </head>    <body>        <h1>Hello World!</h1>    </body></html>

    然后创建 PageController 并添加内容:

    @Controllerpublic class PageController {    @RequestMapping("index.html")    public String index(){        return "index";    }}

    启动 Application.java,访问:http://localhost:8081/api/index.html,就可以看到如图所示:

    enter image description here

    第03课:Spring Boot 启动原理第04课:初识 Spring Cloud
    第05课:服务注册与发现
    第06课:服务网关
    第07课:服务消费者
    第08课:服务异常处理
    第09课:配置中心
    第10课:消息总线
    第11课:服务链路追踪
    第12课:分布式锁
    第13课:分布式事务
    第14课:Spring Cloud 实例详解——基础框架搭建(一)
    第15课:Spring Cloud 实例详解——基础框架搭建(二)
    第16课:Spring Cloud 实例详解——基础框架搭建(三)
    第17课:Spring Cloud 实例详解——业务代码实现
    第18课:Spring Cloud 实例详解——系统发布
    第19课:Spring Cloud 源码解析
    第20课:K8S+Docker 部署 Spring Cloud 集群

    点击购买,可阅读课程全文

    展开全文
  • SpringCloud快速入门

    万次阅读 多人点赞 2019-03-13 16:39:32
    一、 网站的架构演变 网络架构由最开始的三层mvc渐渐演变。传统的三层架构后来在互联网公司让几百人几千人同时开发一个项目已经变得不可行,并且会产生代码冲突的问题。基于SOA面向服务开发的架构,渐渐产生了...

    一、  网站的架构演变

        网络架构由最开始的三层mvc渐渐演变。传统的三层架构后来在互联网公司让几百人几千人同时开发一个项目已经变得不可行,并且会产生代码冲突的问题。基于SOA面向服务开发的架构,渐渐产生了微服务架构。微服务的架构的特点就是项目拆分成各个子项目,进行解耦操作,提供外部访问接口,属于敏捷开发,其实也可以视为面向接口开发。

        一旦有了多个子项目,比如把淘宝网的订单系统和会员系统分开来看,就回产生如何管理接口、负载均衡、高并发情况下怎么限流断路等问题。那么这就有SpringCloud出现了。

        那么springCloud的组件大概有哪些呢,我先简单介绍下:

    1. Eureka  服务注册中心
    2. 服务消费者 Rest 和 Fegin  --消费实现负载均衡ribbon
    3. 接口网关Zuul
    4. Hystrix  关于服务雪崩的解决方案--服务熔断、服务降级、隔离资源。

    二、  Eureka

        eureka是个什么东西呢?它是一个服务注册中心。就拿上面的例子来说,如果要查看会员的订单详情,那么就要在会员系统的tomcat里面调用订单系统的tomcat里的方法。那么直接通过接口访问吗?显然这是不安全的。因此我们需要一个统一管理远程RPC调用的注册中心

    如图所示,会员系统和订单都是独立能够运行的SpringBoot项目,把SpringBoot注册进eureka中,这样我们就可以通过eureka让会员系统远程调用订单系统。具体配置要怎么做呢?

        首先我们要创建eureka注册中心,这里建议用idea的工具创建SpringBoot项目。

    选择如图的包,如果没有则直接复制pom文件

    <?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">
        <modelVersion>4.0.0</modelVersion>
        <parent>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-parent</artifactId>
            <version>2.1.3.RELEASE</version>
            <relativePath/> <!-- lookup parent from repository -->
        </parent>
        <groupId>my</groupId>
        <artifactId>learning</artifactId>
        <version>0.0.1-SNAPSHOT</version>
        <name>learning</name>
        <description>Demo project for Spring Boot</description>
    
        <properties>
            <java.version>1.8</java.version>
            <spring-cloud.version>Greenwich.SR1</spring-cloud.version>
        </properties>
    
        <dependencies>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-web</artifactId>
            </dependency>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
            </dependency>
    
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-test</artifactId>
                <scope>test</scope>
            </dependency>
        </dependencies>
    
        <dependencyManagement>
            <dependencies>
                <dependency>
                    <groupId>org.springframework.cloud</groupId>
                    <artifactId>spring-cloud-dependencies</artifactId>
                    <version>${spring-cloud.version}</version>
                    <type>pom</type>
                    <scope>import</scope>
                </dependency>
            </dependencies>
        </dependencyManagement>
    
        <build>
            <plugins>
                <plugin>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-maven-plugin</artifactId>
                </plugin>
            </plugins>
        </build>
    
        <repositories>
            <repository>
                <id>spring-milestones</id>
                <name>Spring Milestones</name>
                <url>https://repo.spring.io/milestone</url>
            </repository>
        </repositories>
    
    </project>
    

     

    然后我们去yml文件中做如下配置

    #eureka的端口号
    server:
      port: 8888
    eureka:
      instance:
        hostname: localhost
      client:
        registerWithEureka: false
        fetchRegistry: false
        serviceUrl:
          defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/

    然后在启动类里添加表示为eureka注册中心

    @EnableEurekaServer
    @SpringBootApplication
    public class LearningApplication {
    
        public static void main(String[] args) {
            SpringApplication.run(LearningApplication.class, args);
        }
    
    }

    通过localhost:8888可以进到如下页面就表示eureka注册中心启动成功

    OK,那么我们要怎么把订单系统和会员系统注册进去呢?同样创建两个SpringBoot项目,创建方式和导包方式和注册中心一样。我们关注的只有yml文件是如何把这个springBoot项目注册进去的,那么我们来看看yml文件如何配置

    eureka:
      client:
        serviceUrl:
    #      eureka的注册中心地址
          defaultZone: http://localhost:8888/eureka/
    server:
    #  此项目端口号
      port: 8889
    spring:
      application:
    #    注册进eureka的名字
        name: order-server

     

    创建controller包并且启动

    @RestController
    public class ordercontroller {
        @RequestMapping("orderTest")
        public String orderTest(){
            return "this is order";
        }
    }
    
    //  启动类
    @EnableEurekaClient   
    @SpringBootApplication
    public class DemoApplication {
    
        public static void main(String[] args) {
            SpringApplication.run(DemoApplication.class, args);
        }
    
    }

     再次打开注册中心网页,就发现已经注册进去

    重复以上步骤把会员系统也注册进去

    三、  PRC远程调用的方法

        远程调用的方法有两种,我们一一来细说。

        1.  第一种,通过rest的方式来调用,首先我们要导入rest的pom依赖,我们要使用用户调用订单,就在用户里添加调用依赖。先解释一下什么是ribbon-----ribbon是一个负载均衡客户端 类似nginx反向代理,可以很好的控制htt和tcp的一些行为。

    <dependency>

    <groupId>org.springframework.cloud</groupId>

    <artifactId>spring-cloud-starter-ribbon</artifactId>

    </dependency>

    //  在启动类里把ribbon类注入spring
    @EnableEurekaClient
    @SpringBootApplication
    public class DemoApplication {
    
        public static void main(String[] args) {
            SpringApplication.run(DemoApplication.class, args);
        }
        @Bean
        @LoadBalanced        // 开启负载均衡
        public RestTemplate restTemplate(){
            return new RestTemplate();
        }
    }
    
    public class memService{
        @Autowired
        private RestTemplate restTemplate;
        @RequestMapping("/memTest")
        public String memTest(){
            String str = restTemplate.getForObject("http://order-server/orderTest",String.class);
            return str;
        }
    }

    然后我们调用会员系统的接口访问,看他会不会走到订单系统里

    这就是Rest远程调用的结果。

        2.  Feigin

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

    简而言之:

    ·Feign 采用的是基于接口的注解

    ·Feign 整合了ribbon

       第一步依然是导入依赖

    <dependency>

    <groupId>org.springframework.cloud</groupId>

    <artifactId>spring-cloud-starter-feign</artifactId>

    </dependency>

    @Service
    @FeignClient("order-server")
    public interface orderFeign {
        @RequestMapping("/orderTest")
        public String orderTest();
    }
    @RestController
    public class memController {
        @Autowired
        private OrderFeign orderFeign;
        @RequestMapping("/memTest")
        public String memTest(){
            String str = orderFeign.orderTest();
            return str;
        }
    }
    
    // 启动类
    @EnableEurekaClient
    @EnableFeignClients
    @SpringBootApplication
    public class DemoApplication {
    
        public static void main(String[] args) {
            SpringApplication.run(DemoApplication.class, args);
        }
    
    }

    因此成功。这两个方式使用ribbon均衡负载,一个需要手动启动,fegin是自动启动。

    四、  路由网关(ZUUL)

        路由网关有什么作用呢?上面订单和会员系统已经注册进服务中心,两者之间是通过网址直接访问。但是如果在浏览器里由订单访问会员,会因为域名不同而导致跨域问题。跨域问题的解决方案可以使用http client设置、设置请求头、nginx转发解决,那么在SpringCloud里面当然提供了一套解决方案,那就是网关ZUUL。

    如图所示,当一个客户端如果直接访问时,会因为域名不同导致跨域问题。而我们所需要做的就是在前面设置一个网关变成my.com/vip  my.com/order,这样就不会产生跨域的问题。接下来我们来设置zuul。

        第一步首先也要注册进eureka,导入依赖。

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

     第二步配置yml文件..

    #注册进eureka
    eureka:
      client:
        serviceUrl:
          defaultZone: http://localhost:8888/eureka/
    #配置网关端口号
    server:
      port: 8080
    spring:
      application:
        name: zuul-server
    #配置网关转发详情
    zuul:
      routes:
        api-a:
          path: /member/**
          service-id: member-server
        api-b:
          path: /order/**
          service-id: order-server
    //  开启网关
    @EnableZuulProxy
    @SpringBootApplication
    public class DemoApplication {
    
        public static void main(String[] args) {
            SpringApplication.run(DemoApplication.class, args);
        }
    
    }

     访问配置的网关地址8080,调用member-server里的方法,成功!还可以使用网关过滤信息。具体怎样过滤不做赘述。

    五、  断路器(Hystrix)

    为什么需要 Hystrix?

    在微服务架构中,我们将业务拆分成一个个的服务,服务与服务之间可以相互调用(RPC)。为了保证其高可用,单个服务又必须集群部署。由于网络原因或者自身的原因,服务并不能保证服务的100%可用,如果单个服务出现问题,调用这个服务就会出现网络延迟,此时若有大量的网络涌入,会形成任务累计,导致服务瘫痪,甚至导致服务“雪崩”。为了解决这个问题,就出现断路器模型。

     

    什么是服务雪崩

    分布式系统中经常会出现某个基础服务不可用造成整个系统不可用的情况, 这种现象被称为服务雪崩效应. 为了应对服务雪崩, 一种常见的做法是手动服务降级. 而Hystrix的出现,给我们提供了另一种选择.

    通俗来说: 就是对一个方法的PRC调用并发数量太大

     

     

    服务雪崩应对策略

    针对造成服务雪崩的不同原因, 可以使用不同的应对策略:

    1. 流量控制

    2. 改进缓存模式

    3. 服务自动扩容

    服务调用者降级服务

     

    流量控制 的具体措施包括:

    ·网关限流

    ·用户交互限流

    ·关闭重试

     

    什么是服务降级

    所有的RPC技术里面服务降级是一个最为重要的话题,所谓的降级指的是当服务的提供方不可使用的时候,程序不会出现异常,而会出现本地的操作调用。

    通俗解释来说:就是上面例子里的会员系统访问订单系统,执行远程RPC调用方法,但是当达到一定并发量的时候,比如200个人同时访问 orderTest()方法时,tomcat的容量设置的只有150个,剩余的50个人就在外面等待一直等待。服务降级就是不让他们一直等待,调用本地的方法来fallback消息。而不再去PRC方法。

    Hystrix的作用

    1.断路器机制

    断路器很好理解, 当Hystrix Command请求后端服务失败数量超过一定比例(默认50%), 断路器会切换到开路状态(Open). 这时所有请求会直接失败而不会发送到后端服务. 断路器保持在开路状态一段时间后(默认5秒), 自动切换到半开路状态(HALF-OPEN). 这时会判断下一次请求的返回情况, 如果请求成功, 断路器切回闭路状态(CLOSED), 否则重新切换到开路状态(OPEN). Hystrix的断路器就像我们家庭电路中的保险丝, 一旦后端服务不可用, 断路器会直接切断请求链, 避免发送大量无效请求影响系统吞吐量, 并且断路器有自我检测并恢复的能力.

    2.Fallback

    Fallback相当于是降级操作. 对于查询操作, 我们可以实现一个fallback方法, 当请求后端服务出现异常的时候, 可以使用fallback方法返回的值. fallback方法的返回值一般是设置的默认值或者来自缓存.

    3.资源隔离

    在Hystrix中, 主要通过线程池来实现资源隔离. 通常在使用的时候我们会根据调用的远程服务划分出多个线程池. 例如调用产品服务的Command放入A线程池, 调用账户服务的Command放入B线程池. 这样做的主要优点是运行环境被隔离开了. 这样就算调用服务的代码存在bug或者由于其他原因导致自己所在线程池被耗尽时, 不会对系统的其他服务造成影响. 但是带来的代价就是维护多个线程池会对系统带来额外的性能开销. 如果是对性能有严格要求而且确信自己调用服务的客户端代码不会出问题的话, 可以使用Hystrix的信号模式(Semaphores)来隔离资源.

    第一步首先是导入依赖

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

    Rest方式调用

    @HystrixCommand(fallbackMethod = "testError")
    @RequestMapping("/memTest")
        public String memTest(){
            String str = restTemplate.getForObject("http://order-server/orderTest",String.class);
            return str;
        }
        public String testError(){
            //远程调用失败,调用此方法
        }
    
    
    //启动方式
    @EnableEurekaClient
    @EnableHystrix
    @SpringBootApplication
    public class MemApp {
    
    	public static void main(String[] args) {
    		SpringApplication.run(OrderApp.class, args);
    	}

    Fegin方式调用

        yml文件新增配置

    feign:

       hystrix:

         enabled: true

        注册一个继承了Fegin接口的类到Spring容器中

    @Component
    public class MemberFeignService implements orderFeign {
    
    	public String errorMsg {
    		return "出错啦";
    	}
    }
    
    @Service
    @FeignClient("order-server",fallback=MemberFeignService.class)
    public interface orderFeign {
        @RequestMapping("/orderTest")
        public String orderTest();
    }

      所以整个流程就是并发访问量太大导致服务雪崩。然后出发PRC的熔断机制。最后会根据情况来进行隔离资源。

    展开全文
  • SpringCloud微服务核心技术精讲

    千人学习 2019-12-13 21:19:17
    本课程总计13大章节,115课时,是一门全面的SpringCloud微服务体系化课程。课程共包括十三个大章节,涵盖注册中心、网关、熔断、降级、监控、安全、限流等全部体系。包含阿里巴巴Nacos,Consul,Spring Cloud ...
  • 史上最简单的 SpringCloud 教程 | 终章

    万次阅读 多人点赞 2017-04-12 23:14:39
    本文出自方志朋的博客 错过了这一篇,你可能再也学...Spring Boot做为下一代 web 框架,Spring Cloud 作为最新最火的微服务的翘楚,你还有什么理由拒绝。赶快上船吧,老船长带你飞。终章不是最后一篇,它是一个...

    转载请标明出处:
    http://blog.csdn.net/forezp/article/details/70148833
    本文出自方志朋的博客

    avatar.jpg
    扫码关注有惊喜

    (转载本站文章请注明作者和出处 方志朋的博客

    个人博客纯净版https://www.fangzhipeng.com/spring-cloud.html

    错过了这一篇,你可能再也学不会 Spring Cloud 了!Spring Boot做为下一代 web 框架,Spring Cloud 作为最新最火的微服务的翘楚,你还有什么理由拒绝。赶快上船吧,老船长带你飞。终章不是最后一篇,它是一个汇总,未来还会写很多篇。

    我为什么这些文章?一是巩固自己的知识,二是希望有更加开放和与人分享的心态,三是接受各位大神的批评指教,有任何问题可以联系我: miles02@163.com .

    码农下载:https://git.oschina.net/forezp/SpringCloudLearning

    github下载:https://github.com/forezp/SpringCloudLearning,记得star哦!

    欢迎大家访问我的个人博客:https://www.fangzhipeng.com/

    点击获取SpringCloud 、Spring Boot视频

    《史上最简单的 SpringCloud 教程》系列:

    Spring Cloud Alibaba教程

    Greenwich版本

    Finchley版本

    Spring Cloud Finchley; Spring Boot 2.0.3

    源码篇:

    进阶篇

    D版本

    番外篇:

    怎么支持我?

    • 这个系列会持续更新,敬请关注!

    • 关注我的公众号,精彩内容不能错过!

    avatar.jpg
    扫码关注有惊喜

    (转载本站文章请注明作者和出处 方志朋的博客

    展开全文
  • SpringCloud详解

    万次阅读 多人点赞 2018-05-02 13:44:09
    网站架构演变集群:不同的机器,执行同一个计算问题。 一台机器累如狗,怎么办?来两台,负载均衡。 一台机器宕机,怎么办?来两台,灾备容错。 一台机器性能好,怎么办?让性能好的,加权轮询。...

    网站架构演变

    集群:不同的机器,执行同一个计算问题。

    •    一台机器累如狗,怎么办?来两台,负载均衡。
    •     一台机器宕机,怎么办?来两台,灾备容错。
    •     一台机器性能好,怎么办?让性能好的,加权轮询。

    分布式:把一个复杂的计算问题,拆分多个子计算,分布到不同机器上,使其并行执行。

    •     一个计算问题计算过长,怎么办?拆分成子计算,在不同机器并行执行,缩短计算时长。
    •     一个计算问题涉及另一个机器的资源,怎么办?拷贝过来?那我100台都需要呢?所以,需要远程调用和协作。

    为什么使用微服务?

    下面借用Dubbo的网站架构发展说起。


    单一应用架构:将所有功能都部署在一起,以减少部署节点和成本。

    垂直应用架构:当访问量大了,单一应用增加机器带来的加速越来越小,把应用拆分互不相干的的几个应用,以提高效率。

    分布式框架:当垂直应用过多,应用之间交互不可避免,将核心业务抽取,作为独立服务,逐渐形成稳定的服务中心,使前端能更快的响应市场的变化。

    流动计算框架:当服务越来越多,此时需要增加一个调度中心基于访问的压力实时管理集群,提高集群的利用率。

    微服务和面向服务(SOA)区别


    借用蚂蚁课堂余老师课程的一张图说话,微服务与面向服务架构本质上都是分布式面向服务架构,区别在于,面向服务架构(SOA)主要针对银行xml格式 企业级 ESB服务 重量级,如webservice远程调用(HTTP+XML),而微服务更加微小,轻量采用HTTP+Json+RESTful风格 轻量级 独立运行。

    SpringCloud概述

    谈到SpringCloud之前,先看看微服务带来的问题,接口地址如何管理、容错机制、负载均衡、网关、路由策略、高并发情况下,怎么接口限流、断路(服务宕机如何处理)。

    微服务解决框架-SpringCloud、Dubbo

    SpringBoot和SpringCloud的关系

    •     SpringBoot简化xml配置,快速整合框架
    •     SpringCloud是一整套微服务解决方案--RPC远程调用

                关系SpringBoot的SpringMVC暴露接口,而SpringCloud管理接口,存在依赖关系。

    SpringCloud解决问题

        配置管理(注册中心Eureka,Dubbo的是Zookeeper)、服务发现、服务注册、断路器(非常重要,容错机制、限流、调用接口失败如何解决)、路由策略(接口分发那台机器上去)、负载均衡、全局锁、分布式会话、客户端调用、接口网关、服务管理系统。

    服务注册和发现(Eureka)

    什么是Eureka

    官方的介绍在这里Eureka wiki。Eureka是Netflix开源的一个RESTful服务,主要用于服务的注册发现。Eureka由两个组件组成:Eureka服务器和Eureka客户端。Eureka服务器用作服务注册服务器。Eureka客户端是一个java客户端,用来简化与服务器的交互、作为轮询负载均衡器,并提供服务的故障切换支持。Netflix在其生产环境中使用的是另外的客户端,它提供基于流量、资源利用率以及出错状态的加权负载均衡。
    Eureka的吸引力来源于以下几点:

    • 开源:大家可以对实现一探究竟,甚至修改源码。
    • 可靠:经过Netflix多年的生产环境考验,使用应该比较靠谱省心
    • 功能齐全:不但提供了完整的注册发现服务,还有Ribbon等可以配合使用的服务。
    • 基于Java:对于Java程序员来说,使用起来,心里比较有底。
    • spring cloud可以使用Spring Cloud, 与Eureka进行了很好的集成,使用起来非常方便。

    搭建注册中心、服务提供者、服务消费者

    实现服务注册

    1.创建Eurekaserver项目

    2.Maven依赖

            <parent>
    		<groupId>org.springframework.boot</groupId>
    		<artifactId>spring-boot-starter-parent</artifactId>
    		<version>1.5.2.RELEASE</version>
    		<relativePath /> <!-- lookup parent from repository -->
    	</parent>
    	<properties>
    		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    		<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
    		<java.version>1.8</java.version>
    	</properties>
    
    	<dependencies>
    		<!--eureka server -->
    		<dependency>
    			<groupId>org.springframework.cloud</groupId>
    			<artifactId>spring-cloud-starter-eureka-server</artifactId>
    		</dependency>
    		<!-- spring boot test -->
    		<dependency>
    			<groupId>org.springframework.boot</groupId>
    			<artifactId>spring-boot-starter-test</artifactId>
    			<scope>test</scope>
    		</dependency>
    	</dependencies>
    	<dependencyManagement>
    		<dependencies>
    			<dependency>
    				<groupId>org.springframework.cloud</groupId>
    				<artifactId>spring-cloud-dependencies</artifactId>
    				<version>Dalston.RC1</version>
    				<type>pom</type>
    				<scope>import</scope>
    			</dependency>
    		</dependencies>
    	</dependencyManagement>
    	<build>
    		<plugins>
    			<plugin>
    				<groupId>org.springframework.boot</groupId>
    				<artifactId>spring-boot-maven-plugin</artifactId>
    			</plugin>
    		</plugins>
    	</build>
    	<repositories>
    		<repository>
    			<id>spring-milestones</id>
    			<name>Spring Milestones</name>
    			<url>https://repo.spring.io/milestone</url>
    			<snapshots>
    				<enabled>false</enabled>
    			</snapshots>
    		</repository>
    	</repositories>

    3.配置application.yml

    server:
      port: 8888
    eureka:
      instance:
        hostname: localhost
      client:
        registerWithEureka: false
        fetchRegistry: false
        serviceUrl:
          defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/

    4.启动EurekaServer

    @SpringBootApplication
    @EnableEurekaServer
    public class App {
    	public static void main(String[] args) {
    		SpringApplication.run(App.class, args);
    	}
    }
    

    5.打开EurekaServer界面


    可以看到没有服务注册,我们现在新建一个服务类注册服务上去
    服务提供者

    创建一个服务提供者 会员服务工程,提供会员查询服务信息。

    1.创建项目service-member

    2.Maven依赖

    <parent>
    		<groupId>org.springframework.boot</groupId>
    		<artifactId>spring-boot-starter-parent</artifactId>
    		<version>1.5.2.RELEASE</version>
    		<relativePath /> <!-- lookup parent from repository -->
    	</parent>
    	<properties>
    		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    		<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
    		<java.version>1.8</java.version>
    	</properties>
    	<dependencies>
    		<dependency>
    			<groupId>org.springframework.cloud</groupId>
    			<artifactId>spring-cloud-starter-eureka</artifactId>
    		</dependency>
    		<dependency>
    			<groupId>org.springframework.boot</groupId>
    			<artifactId>spring-boot-starter-web</artifactId>
    		</dependency>
    		<dependency>
    			<groupId>org.springframework.boot</groupId>
    			<artifactId>spring-boot-starter-test</artifactId>
    			<scope>test</scope>
    		</dependency>
    	</dependencies>
    	<dependencyManagement>
    		<dependencies>
    			<dependency>
    				<groupId>org.springframework.cloud</groupId>
    				<artifactId>spring-cloud-dependencies</artifactId>
    				<version>Dalston.RC1</version>
    				<type>pom</type>
    				<scope>import</scope>
    			</dependency>
    		</dependencies>
    	</dependencyManagement>
    	<build>
    		<plugins>
    			<plugin>
    				<groupId>org.springframework.boot</groupId>
    				<artifactId>spring-boot-maven-plugin</artifactId>
    			</plugin>
    		</plugins>
    	</build>
    	<repositories>
    		<repository>
    			<id>spring-milestones</id>
    			<name>Spring Milestones</name>
    			<url>https://repo.spring.io/milestone</url>
    			<snapshots>
    				<enabled>false</enabled>
    			</snapshots>
    		</repository>
    	</repositories>
    

    3.application.yml配置

    eureka:
      client:
        serviceUrl:
          defaultZone: http://localhost:8888/eureka/
    server:
      port: 8762
    spring:
      application:
        name: service-member
    

    4.服务接口

    @RestController
    public class MemberController {
    
    	@RequestMapping("/getUserList")
    	public List<String> getUserList() {
    		List<String> listUser = new ArrayList<String>();
    		listUser.add("zhangsan");
    		listUser.add("lisi");
    		listUser.add("yushengjun");
    		return listUser;
    	}
    }
    

    5.发布服务

    通过注解@EnableEurekaClient表明自己是一个eurekaClient

    @SpringBootApplication
    @EnableEurekaClient
    public class AppMember {
    	public static void main(String[] args) {
    		SpringApplication.run(AppMember.class, args);
    	}
    }
    

    6.演示效果

    服务名称已经注册到Eureka上了


    服务消费者

    1.创建项目service-order

    2.Maven依赖

    <parent>
    		<groupId>org.springframework.boot</groupId>
    		<artifactId>spring-boot-starter-parent</artifactId>
    		<version>1.5.2.RELEASE</version>
    		<relativePath /> <!-- lookup parent from repository -->
    	</parent>
    
    	<properties>
    		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    		<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
    		<java.version>1.8</java.version>
    	</properties>
    
    	<dependencies>
    		<dependency>
    			<groupId>org.springframework.cloud</groupId>
    			<artifactId>spring-cloud-starter-eureka</artifactId>
    		</dependency>
    		<dependency>
    			<groupId>org.springframework.cloud</groupId>
    			<artifactId>spring-cloud-starter-ribbon</artifactId>
    		</dependency>
    		<dependency>
    			<groupId>org.springframework.boot</groupId>
    			<artifactId>spring-boot-starter-web</artifactId>
    		</dependency>
    
    		<dependency>
    			<groupId>org.springframework.boot</groupId>
    			<artifactId>spring-boot-starter-test</artifactId>
    			<scope>test</scope>
    		</dependency>
    	</dependencies>
    
    	<dependencyManagement>
    		<dependencies>
    			<dependency>
    				<groupId>org.springframework.cloud</groupId>
    				<artifactId>spring-cloud-dependencies</artifactId>
    				<version>Dalston.RC1</version>
    				<type>pom</type>
    				<scope>import</scope>
    			</dependency>
    		</dependencies>
    	</dependencyManagement>
    
    	<build>
    		<plugins>
    			<plugin>
    				<groupId>org.springframework.boot</groupId>
    				<artifactId>spring-boot-maven-plugin</artifactId>
    			</plugin>
    		</plugins>
    	</build>
    
    	<repositories>
    		<repository>
    			<id>spring-milestones</id>
    			<name>Spring Milestones</name>
    			<url>https://repo.spring.io/milestone</url>
    			<snapshots>
    				<enabled>false</enabled>
    			</snapshots>
    		</repository>
    	</repositories>
    

    3.application.yml配置

    eureka:
      client:
        serviceUrl:
          defaultZone: http://localhost:8888/eureka/
    server:
      port: 8764
    spring:
      application:
        name: service-order
    

    4.编写service,调用server-member

    @SuppressWarnings("unchecked")
    @Service
    public class MemberService {
    	@Autowired
    	RestTemplate restTemplate;
    
    	public List<String> getOrderByUserList() {
    		return restTemplate.getForObject("http://service-member/getUserList", List.class);
    	}	
    }
    

    5.编写controller,调用service。

    @RestController
    public class MemberController {
    	@Autowired
    	MemberService memberService;
    	@RequestMapping("/getOrderUserAll")
    	public List<String> getOrderUserList(){
    		return memberService.getOrderByUserList();
    	}
    }

    6.发布

    @EnableEurekaClient
    @SpringBootApplication
    public class OderApp {
    	public static void main(String[] args) {
    		SpringApplication.run(OderApp.class, args);
    	}
    	/**
    	 * 必须加上,不然启动不了
    	 * @return
    	 */
    	@Bean	//加入bean容器中
    	@LoadBalanced	//支持负载均衡
    	RestTemplate restTemplate() {
    		return new RestTemplate();
    	}
    }
    

    这时候可以消费服务,并且Eureka注册中心也多了一个service-order服务


    SpringCloud调用服务原理


    会员服务注册到注册中心里面,订单服务获取真实的访问地址,通过httpclient去访问会员服务获取数据。

    SpringCloud实现负载均衡

    怎么实现负载均衡nginx、lvs、HAproxy、F5

    SpringCloud中负载均衡,使用Rebbion技术


    什么是Rebbion

    ribbion是一个负载均衡客户端,类似nginx反向代理,可以很好的控制http和tcp的一些行为。Feign默认集成rebbion。

    使用Rebbion负载均衡

    1.修改会员服务工程代码区分端口项目

    @Value("${server.port}")
    	private String serverport;
    	@RequestMapping("/getUserList")
    	public ArrayList<String> getUserList() {
    		ArrayList<String> listUser = new ArrayList<String>();
    		listUser.add("zhangsan");
    		listUser.add("lisi");
    		listUser.add("流浪者");
    		listUser.add("端口号"+serverport);
    		return listUser;
    	}

    2.启动两次会员工程项目,并且端口号不一致。


    这时候会发现,service-member服务有两个。

    3.引入Maven依赖

    <dependency><!--ribbon用于负载均衡-->
    			<groupId>org.springframework.cloud</groupId>
    			<artifactId>spring-cloud-starter-ribbon</artifactId>
    		</dependency>

    4.在启动类下,把restTemplate加入bean容器,并且上上注解LoadBalanced

    @EnableEurekaClient
    @SpringBootApplication
    public class OderApp {
    	public static void main(String[] args) {
    		SpringApplication.run(OderApp.class, args);
    	}
    	/**
    	 * 必须加上,不然启动不了
    	 * @return
    	 */
    	@Bean	//加入bean容器中
    	@LoadBalanced	//支持负载均衡
    	RestTemplate restTemplate() {
    		return new RestTemplate();
    	}
    }

    5.演示效果

    第一次访问9762


    第二次访问


    什么是接口网关

    前面说了,我们通过httpclient、ajax访问服务,但是在访问不同接口会出现跨域和内网问题。这时候就需要接口网关。

    路由网关(zuul)

    zuul的主要作用是路由转发和过滤器,路由功能是微服务的一部分,比如/api/user/转发到user服务。

    搭建SpringCloud网关

    1.创建service-zuul工程

    2.Maven依赖

    <parent>
    		<groupId>org.springframework.boot</groupId>
    		<artifactId>spring-boot-starter-parent</artifactId>
    		<version>1.5.2.RELEASE</version>
    		<relativePath /> <!-- lookup parent from repository -->
    	</parent>
    
    	<properties>
    		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    		<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
    		<java.version>1.8</java.version>
    	</properties>
    
    	<dependencies>
    		<dependency>
    			<groupId>org.springframework.cloud</groupId>
    			<artifactId>spring-cloud-starter-eureka</artifactId>
    		</dependency>
    		<dependency>
    			<groupId>org.springframework.cloud</groupId>
    			<artifactId>spring-cloud-starter-zuul</artifactId>
    		</dependency>
    		<dependency>
    			<groupId>org.springframework.boot</groupId>
    			<artifactId>spring-boot-starter-web</artifactId>
    		</dependency>
    
    		<dependency>
    			<groupId>org.springframework.boot</groupId>
    			<artifactId>spring-boot-starter-test</artifactId>
    			<scope>test</scope>
    		</dependency>
    	</dependencies>
    
    	<dependencyManagement>
    		<dependencies>
    			<dependency>
    				<groupId>org.springframework.cloud</groupId>
    				<artifactId>spring-cloud-dependencies</artifactId>
    				<version>Dalston.RC1</version>
    				<type>pom</type>
    				<scope>import</scope>
    			</dependency>
    		</dependencies>
    	</dependencyManagement>
    
    	<build>
    		<plugins>
    			<plugin>
    				<groupId>org.springframework.boot</groupId>
    				<artifactId>spring-boot-maven-plugin</artifactId>
    			</plugin>
    		</plugins>
    	</build>
    
    	<repositories>
    		<repository>
    			<id>spring-milestones</id>
    			<name>Spring Milestones</name>
    			<url>https://repo.spring.io/milestone</url>
    			<snapshots>
    				<enabled>false</enabled>
    			</snapshots>
    		</repository>
    	</repositories>
    

    3.application.yml配置

    eureka:
      client:
        serviceUrl:
          defaultZone: http://localhost:8888/eureka/
    server:
      port: 8769
    spring:
      application:
        name: service-zuul
    zuul:
      routes:
        api-a:
          path: /api-member/**
          service-id: service-member
        api-b:
          path: /api-order/**
          service-id: service-order
    

    4.启动类

    @EnableZuulProxy
    @EnableEurekaClient
    @SpringBootApplication
    public class AppZuul {
    	public static void main(String[] args) {
    		SpringApplication.run(AppZuul.class, args);
    	}
    }
    5.演示效果  通过Zuul网关转发到实际会员服务。


    服务过滤
    @Component
    public class MyFilter extends ZuulFilter {
    
    	private static Logger log = LoggerFactory.getLogger(MyFilter.class);
    
    	@Override
    	public String filterType() {
    		return "pre";
    	}
    
    	@Override
    	public int filterOrder() {
    		return 0;
    	}
    
    	public boolean shouldFilter() {
    		return true;
    	}
    
    	public Object run() {
    		RequestContext ctx = RequestContext.getCurrentContext();
    		HttpServletRequest request = ctx.getRequest();
    		log.info(String.format("%s >>> %s", request.getMethod(), request.getRequestURL().toString()));
    		Object accessToken = request.getParameter("token");
    		if (accessToken != null) {
    			return null;
    		}
    		log.warn("token is empty");
    		ctx.setSendZuulResponse(false);
    		ctx.setResponseStatusCode(401);
    		try {
    			ctx.getResponse().getWriter().write("token is empty");
    		} catch (Exception e) {
    		}
    		return null;
    
    	}
    }

    分布式配置中心概述(SpringCloud Config)

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


    上面的话是否有些懵逼,说说应用场景,比如说数据库配置文件,在开发环境(dev),在测试环境(test),在正式环境中(pro)都是不一样的,如何切换,为不同服务统一管理,这就需要分布式配置中心。

    config server:相当于git中的本地仓库,熟悉git的同学知道,git从远程仓库pull资源,在本地修改。

    搭建分布式配置中心    service-config项目

    1.创建git地址

    https://github.com/mingchangkun/SpringCloud.git

    2.config-client-dev.properties    --dev环境

    3.上传配置文件

    name=小明
    pwd=123456

    上传git命令

    git init
    git add README.md
    git commit -m "first commit"
    git remote add origin https://github.com/mingchangkun/SpringCloud.git
    git push -u origin master

    4.创建config-server项目    相当于本地仓库

    5.Maven依赖

    	<parent>
    		<groupId>org.springframework.boot</groupId>
    		<artifactId>spring-boot-starter-parent</artifactId>
    		<version>1.5.2.RELEASE</version>
    		<relativePath /> <!-- lookup parent from repository -->
    	</parent>
    
    	<properties>
    		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    		<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
    		<java.version>1.8</java.version>
    	</properties>
    
    	<dependencies>
    		<dependency>
    			<groupId>org.springframework.cloud</groupId>
    			<artifactId>spring-cloud-config-server</artifactId>
    		</dependency>
    
    		<dependency>
    			<groupId>org.springframework.boot</groupId>
    			<artifactId>spring-boot-starter-test</artifactId>
    			<scope>test</scope>
    		</dependency>
    
    		<dependency>
    			<groupId>org.springframework.cloud</groupId>
    			<artifactId>spring-cloud-starter-eureka</artifactId>
    		</dependency>
    	</dependencies>
    
    	<dependencyManagement>
    		<dependencies>
    			<dependency>
    				<groupId>org.springframework.cloud</groupId>
    				<artifactId>spring-cloud-dependencies</artifactId>
    				<version>Camden.SR6</version>
    				<type>pom</type>
    				<scope>import</scope>
    			</dependency>
    		</dependencies>
    	</dependencyManagement>
    
    
    	<build>
    		<plugins>
    			<plugin>
    				<groupId>org.springframework.boot</groupId>
    				<artifactId>spring-boot-maven-plugin</artifactId>
    			</plugin>
    		</plugins>
    	</build>
    
    	<repositories>
    		<repository>
    			<id>spring-milestones</id>
    			<name>Spring Milestones</name>
    			<url>https://repo.spring.io/milestone</url>
    			<snapshots>
    				<enabled>false</enabled>
    			</snapshots>
    		</repository>
    	</repositories>
    

    6.配置applcation.properties配置文件

    spring.application.name=config-server
    server.port=8889
    spring.cloud.config.server.git.uri=https://github.com/mingchangkun/SpringCloud.git
    spring.cloud.config.server.git.searchPaths=
    spring.cloud.config.label=master
    spring.cloud.config.server.git.username=
    spring.cloud.config.server.git.password=
    eureka.client.serviceUrl.defaultZone=http://localhost:8888/eureka/

    • spring.cloud.config.server.git.uri:配置git仓库地址
    • spring.cloud.config.server.git.searchPaths:配置仓库路径
    • spring.cloud.config.label:配置仓库的分支
    • spring.cloud.config.server.git.username:访问git仓库的用户名
    • spring.cloud.config.server.git.password:访问git仓库的用户密码

    如果Git仓库为公开仓库,可以不填写用户名和密码,如果是私有仓库需要填写,本例子是公开仓库,放心使用。

    7.运行

    @EnableConfigServer    //分布式配置服务
    @EnableEurekaClient    //注册到Eureka服务中心
    @SpringBootApplication
    public class AppConfig {
    	public static void main(String[] args) {
    		SpringApplication.run(AppConfig.class, args);
    	}
    }

    8.演示  查看http://localhost:8889/foo/dev 查询配置中心    成功搭建config配置中心


    并且服务已经注册到Eureka服务中心了


    创建config-client项目

    1.创建config-client项目

    2.Maven依赖

    <parent>
    		<groupId>org.springframework.boot</groupId>
    		<artifactId>spring-boot-starter-parent</artifactId>
    		<version>1.5.2.RELEASE</version>
    		<relativePath /> <!-- lookup parent from repository -->
    	</parent>
    
    	<properties>
    		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    		<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
    		<java.version>1.8</java.version>
    	</properties>
    
    	<dependencies>
    		<dependency>
    			<groupId>org.springframework.cloud</groupId>
    			<artifactId>spring-cloud-starter-config</artifactId>
    		</dependency>
    
    		<dependency>
    			<groupId>org.springframework.boot</groupId>
    			<artifactId>spring-boot-starter-web</artifactId>
    		</dependency>
    
    		<dependency>
    			<groupId>org.springframework.boot</groupId>
    			<artifactId>spring-boot-starter-test</artifactId>
    			<scope>test</scope>
    		</dependency>
    	</dependencies>
    
    	<dependencyManagement>
    		<dependencies>
    			<dependency>
    				<groupId>org.springframework.cloud</groupId>
    				<artifactId>spring-cloud-dependencies</artifactId>
    				<version>Dalston.RC1</version>
    				<type>pom</type>
    				<scope>import</scope>
    			</dependency>
    		</dependencies>
    	</dependencyManagement>
    
    	<build>
    		<plugins>
    			<plugin>
    				<groupId>org.springframework.boot</groupId>
    				<artifactId>spring-boot-maven-plugin</artifactId>
    			</plugin>
    		</plugins>
    	</build>
    
    	<repositories>
    		<repository>
    			<id>spring-milestones</id>
    			<name>Spring Milestones</name>
    			<url>https://repo.spring.io/milestone</url>
    			<snapshots>
    				<enabled>false</enabled>
    			</snapshots>
    		</repository>
    	</repositories>
    

    3.bootstrap.properties配置文件

    spring.application.name=config-client
    spring.cloud.config.label=master
    spring.cloud.config.profile=
    #spring.cloud.config.uri= http://localhost:8888/
    
    eureka.client.serviceUrl.defaultZone=http://localhost:8888/eureka/
    spring.cloud.config.discovery.enabled=true
    spring.cloud.config.discovery.serviceId=config-server
    server.port=8881

    • spring.cloud.config.discovery.enabled 是从配置中心读取文件。
    • spring.cloud.config.discovery.serviceId 配置中心的servieId,即服务名。

    这时发现,在读取配置文件不再写ip地址,而是服务名,这时如果配置服务部署多份,通过负载均衡,从而高可用

    4.使用分布式配置文件

    @RestController
    public class UserController {
    	@Value("${name}")
    	String name;
    	@Value("${pwd}")
    	String pwd;
    	@RequestMapping("/getUserName")
    	public String getUsername(){
    		return "姓名:"+name+",密码"+pwd;
    	}
    }

    5.启动

    @SpringBootApplication
    @EnableEurekaClient
    public class AppClient {
    	public static void main(String[] args) {
    		SpringApplication.run(AppClient.class, args);
    	}
    }
    

    5.访问服务


    参考资料

        CSDN最全SpringCloud系列        https://blog.csdn.net/forezp/article/details/70148833    

        蚂蚁课堂SpringCloud教学          http://www.itmayiedu.com/front/couinfo/91/0



    展开全文
  • SpringCloud详细教程(上)

    万次阅读 多人点赞 2019-07-04 16:30:11
    SpringCloud详细教程,SpringCloud详细教程。SpringCloud是一系列框架的有序集合。如服务发现注册、配置中心、消息总线、负载均衡、断路器、数据监控等,都可以用SpringBoot的开发风格做到一键启动和部署。本文主要...
  • 【小学生系列】Spring Cloud Alibaba 学习笔记一 前言   最近项目一直在使用Spring Cloud Alibaba+Nacos框架,犹豫项目紧张一直想总结,因为后期监控项目数据的问题,好吧 我承认是懒…所以还是做一个系列分享一下...
  • SpringCloud Alibaba 笔记(一) B站视频地址 一,搭建父模块 1,创建普通Maven工程 如果导包爆红去掉 dependencyManagement 标签重新导入后再加回标签 <?xml version="1.0" encoding="UTF-8"?> <project ...
  • SpringCloud&alibaba学习笔记系列 该笔记仅作为个人学习使用。 第一章微服务架构概述及入门 1.1微服务架构概述 ​ 微服务架构是一种架构模式,它提倡将单一应用程序划分为一组小的服务,服务之间互相协调、互相...
  • Spring Cloud Alibaba学习笔记: 1、简介

    千次阅读 2019-07-06 23:16:10
    注: 以下介绍全部来源于官方说明 Spring Cloud Alibaba 致力于提供微服务开发的一站式解决方案。此项目包含开发分布式应用服务的必需组件,方便开发者通过 Spring Cloud 编程模型轻松使用这些组...
  • 概述:最近学完了尚硅谷阳哥的Springcloud第二季,特此记下笔记。 首先,上一张springcloud迭代图。 我
  • SpringCloud简介及使用

    万次阅读 多人点赞 2019-05-03 19:46:54
    SpringCloud简介 Spring cloud是一个基于Spring Boot实现的服务治理工具包,在微服务架构中用于管理和协调服务的 微服务:就是把一个单体项目,拆分为多个微服务,每个微服务可以独立技术选型,独立开发,独立部署,...
  • 搭建SpringCloud项目,并实现自动化部署

    万次阅读 多人点赞 2019-07-11 16:14:47
    项目资源:https://github.com/sunroyi/SpringCloud 主要分为以下几步: (1)搭建SpringBootService,这里是各个微服务的业务逻辑。(这里搭建了2个Service,用来测试熔断) (2)搭建SpringBootEureka,用来...
  • 转载请标明出处: ... 本文出自方志朋的博客 ...鉴于《史上最简单的Spring Cloud教程》很受读者欢迎,再次我特意升级了一下版本,目前支持的版本为Spring Boot版本2.0.3.RELEASE,Spring Cloud版本为F...
  • spring cloud 为开发人员提供了快速构建分布式系统的一些工具,包括配置管理、服务发现、断路器、路由、微代理、事件总线、全局锁、决策竞选、分布式会话等等。它运行环境简单,可以在开发人员的电脑上跑。另外说明...
  • 简介在分布式系统中,spring cloud config 提供一个服务端和客户端去提供可扩展的配置服务。我们可用用配置服务中心区集中的管理所有的服务的各种环境配置文件。配置服务中心采用git的方式存储配置文件,因此我们很...
  • Spring Cloud Bus 将分布式的节点和轻量的消息代理连接起来。这可以用于广播配置文件的更改或者其他的管理工作。一个关键的思想就是,消息总线可以为微服务做监控,也可以作为应用程序之间相互通讯。本文要讲述的是...
  • 比较spring cloud和dubbo,各自的优缺点是什么

    万次阅读 多人点赞 2018-04-19 17:12:24
    dubbo由于是二进制的传输,占用带宽会更少 springCloud是http协议传输,带宽...springcloud的接口协议约定比较自由且松散,需要有强有力的行政措施来限制接口无序升级 dubbo的注册中心可以选择zk,redis等多种,sp...
  • 当服务很多时,都需要同时从配置中心读取文件的时候,这时我们可以考虑将配置中心做成一个微服务,并且将其集群化,从而达到高可用,架构图如下:一、准备工作继续使用上一篇文章的工程,创建一个eureka-server工程...
  • Spring Cloud面试题(2020最新版)

    万次阅读 多人点赞 2020-02-19 17:51:59
    文章目录为什么需要学习Spring Cloud什么是Spring Cloud设计目标与优缺点设计目标优缺点Spring Cloud发展前景整体架构主要项目Spring Cloud ConfigSpring Cloud NetflixSpring Cloud BusSpring Cloud ConsulSpring ...
  • 转载请标明出处: http://blog.csdn.net/forezp/article/details/69788938 本文出自方志朋的博客 在上一篇文章,讲了服务的注册和...Spring cloud有两种调用方式,一种是ribbon+restTemplate,另一种是feign。在这
  • 史上最简单的SpringCloud教程 | 第五篇: 路由网关(zuul)

    万次阅读 多人点赞 2017-04-09 23:25:01
    在微服务架构中,需要几个关键的组件,服务注册与发现、服务消费、负载均衡、断路器、智能路由、配置管理等,由这几个组件可以组建一个简单的微服务架构。客户端的请求首先经过负载均衡(zuul、Ngnix),再到达服务...
  • 在微服务架构中,我们将业务拆分成一个个的服务,服务与服务之间可以相互调用(RPC)。为了保证其高可用,单个服务又必须集群部署。由于网络原因或者自身的原因,服务并不能保证服务的100%可用,如果单个服务出现...
  • SpringCloud之服务网关Gateway

    万次阅读 多人点赞 2019-06-30 22:02:38
    SpringCloud 是微服务中的翘楚,最佳的落地方案。 Spring Cloud Gateway 是 Spring Cloud 新推出的网关框架,之前是Netflix Zuul。网关通常在项目中为了简化 前端的调用逻辑,同时也简化内部服务之间互相调用的...
  • 这篇文章主要讲述通过feign去消费服务。Feign是一个声明式的web服务客户端,它使得写web服务变得更简单。使用Feign,只需要创建一个接口并注解。它具有可插拔的注解特性,包括Feign 注解和JAX-RS注解。...
  • Docker是一个开源的引擎,可以轻松的为任何应用创建一个轻量级的、可移植的、自给自足的容器。开发者在笔记本上编译测试通过的容器可以批量地在生产环境中部署,包括VMs(虚拟机)、bare metal、OpenStack 集群和...
  • 这篇文章主要讲述服务追踪组件zipkin,Spring Cloud Sleuth集成了zipkin组件。
  • SpringCloud面试题(一)

    万次阅读 多人点赞 2019-04-24 22:16:30
    SpringCloud面试题(一) 大家好,我是酷酷的韩~下面提供一些整理的springcloud面试题 一.微服务的优点缺点?说下开发项目中遇到的坑? 优点: 1.每个服务直接足够内聚,代码容易理解 2.开发效率高,一个服务只做一件事...
  • 对于SpringCloud,很多小伙伴问到了我的研究学习资料来源,除官方文档外,特例完整整理一下自己的平时参考学习其他资料,以及分享实战项目源码和代码资源,供大家参考学习 主要教程:SpringCloud教程 Spring ...
  • 转载请标明出处: ... 本文出自方志朋的博客 在上一篇文章讲述zuul的时候,已经...它就是Spring Cloud Config。 一、简介 在分布式系统中,由于服务数量巨多,为了方便服务配置文件统一管理,实时更新,所以需要...

空空如也

1 2 3 4 5 ... 20
收藏数 259,021
精华内容 103,608
关键字:

springcloud

spring 订阅