本篇带大家从入门走向高可用,从屌丝走向高富帅...

1.先搭一个最简单的项目,找点自信:

最简单的spring-cloud项目需要一个注册中心和两个微服务,其他组件后面引入,这里先不提,

注册中心eurka代码入下:

先看依赖: 其中spring-boot版本采用2.0.4,spring-cloud版本采用 Finchley.SR1,不明白的可以去官网:http://spring.io查看.

<dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
</dependencies>
eureka:
  client:
    service-url:
      defaultZone: http://localhost:8761/eureka/  # 指定注册中心的url
    register-with-eureka: false  # 指定不进行注册操作,默认为true,若进行注册的话,会显示在Eureka信息面板上
  server:
    enable-self-preservation: false  # 禁用eureka server的自我保护机制,建议在生产环境下打开此配置
spring:
  application:
    name: eureka-server  # 指定应用的名称
server:
  port: 8761  # 指定项目的端口号

启动类:

@SpringBootApplication
@EnableEurekaServer
public class ServerApplication {

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

服务发现中心搭好了,就是这么简单~

下面来看两个服务:

我就不一一贴源码了,简单说下流程,基本上spring-cloud的套路都是这样的:

第一步:引入依赖,这里需要引入org.springframework.cloud 的 spring-cloud-starter-netflix-eureka-client包

<dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>

第二步:修改配置文件application.yml 在配置文件中指明注册中心,服务端口号,服务名

eureka:
  client:
    service-url:
      defaultZone: http://localhost:8761/eureka/

spring:
  application:
    name: ylt-test

server:
  port: 9111

第三步:启动类上面添加注解@EnableDiscoveryClient,这样服务发现中心Eureka就能找到这个服务了

@SpringBootApplication
@EnableDiscoveryClient
public class DemoApplication {

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

服务调用端的启动类上还需要加上@EnableFeignClients注解,否则在调用服务时会无响应.比如A要调用B,需要在A的启动类上加上该注解:

@SpringBootApplication
@EnableDiscoveryClient
@EnableFeignClients
public class YltDiagnoseApplication {

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

至此spring-cloud两个微服务也搭建好了,这里省略了两个微服务提供的接口,可以自己给每个微服务写个接口,提供的服务可以只是打印一句话.

此时先启动eureka发现中心,再分别启动两个微服务,然后访问注册中心地址,我这里配的是:http://localhost:8761/eureka/,打开浏览器访问,如果可以找到自己配置的服务列表,即表示成功,如图:

springcloud仓储系统库存准确_zipkin

此时我需要在A服务中调用B服务的某个接口,那么我只需要在A服务中添加一个Feign组件:

新建一个接口,名字随意,然后在该接口上添加注解:@FeignClient,注解里的name或者value填写被调用服务的application.name,也就是上图中的application ,然后该接口中的方法名可以随便取,但返回类型一定要与被调用接口的返回数据类型一致,且访问的路径也要一致,这样才能完成调用.

@FeignClient(name = "ylt-diagnose")
public interface DiagnoseClient {
    @GetMapping("/diagnose")
    String getDiagnose();
}

下面我们分别启动A服务和B服务,然后先测试一下直接在B服务中访问该接口:

springcloud仓储系统库存准确_zipkin_02

然后我们再在A服务中调用B服务中的该接口:

springcloud仓储系统库存准确_spring cloud_03

就是这么爽,只需要简单的写个接口,添加个注解,就可以享受到rpc级的调用快感,而且被调用时服务端是无感知的,也就是说A要调用B服务,不需要修改B中的代码,对B而言,A服务调用它的接口和别人直接在浏览器里访问是一样的,没什么差异,这就是spring-cloud的强大之处,当然随着进一步学习你会发现它有多么强大,这仅仅只是冰山一角.

 

2.集成zuul和config,实现统一入口和统一配置及自动拉取配置,这一块内容比较多,建议先去spring官网或者网上找点视频学习一下,否则直接看我贴的demo可能看不懂,我贴这个更多的是为了总结和以后如果用到了快速回忆,免得时间太久不用忘记掉.

先来看网关zuul,也是经典的三部曲:

第一步:添加依赖,这里主要用到 :spring-cloud-starter-netflix-zuul和spring-cloud-starter-netflix-eureka-client,如果需要自动拉取配置的话还需要添加两个依赖:spring-cloud-starter-bus-amqp,spring-cloud-config-client

<dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-bus-amqp</artifactId>
 </dependency>
 <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
 </dependency>

第二步:修改配置文件

spring:
  application:
    name: gateway
  cloud:
    config:
      discovery:
        enabled: true
        service-id: ylt-config #配置中心config的application.name
      profile: dev
eureka:
  client:
    service-url:
      defaultZone: http://localhost:8761/eureka/
server:
  port: 7777
zuul:
  sensitive-headers: #添加后可以保留敏感头,比如cookie

第三步:

启动类添加注解:

@SpringBootApplication
@EnableZuulProxy
@EnableDiscoveryClient
public class GatewayApplication {

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

然后启动测试一下: 输入zuul的ip和端口然后接上其他服务的名称及mapping路径,即可访问到其他服务的接口.

下面是config配置中心:

也是一样的三部曲:

第一步:引入依赖,因为需要自动加载配置,所以用到了rabbitmq,所以需要先在电脑上装个rabbitmq,另外在远端git仓库修改配置后需要触发远端git的webhook,请求本地的/monitor接口来触发修改,所以需要引入spring-cloud-config-monitor依赖.

<dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-bus-amqp</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-config-monitor</artifactId>
        </dependency>
        <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-client</artifactId>
        </dependency>

第二步:修改配置文件

spring:
  application:
    name: eureka-config
  cloud:
    config:
      server:
        git:
          uri: https://github.com/laohanjianshen/ylt #码云中建立对应的仓库
          username: #填你自己的账号
          password: #填你自己的密码
eureka:
  client:
    service-url:
      defaultZone: http://localhost:8761/eureka/

server:
  port: 8888

第三步:启动类添加注解

@SpringBootApplication
@EnableDiscoveryClient
@EnableConfigServer
public class ConfigApplication {

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

然后启动,启动之后在服务发现中心可以找到它,然后git仓库中可以添加对应的配置文件,命名与各个微服务的名字保持一致,比如我有个服务的名字叫zuul,那么我在git仓库中的配置文件名字就应该取为zuul.yml或者zuul-dev.yml等,然后把zuul服务中的公共配置比如数据库连接之类的提取出来放入git中的zuul.yml,并且要把zuul中的配置文件application.yml名字改为bootstrap.yml,这里涉及到一个先后顺序,springboot会先加载bootstrap.yml然后再加载application.yml.注意在application.name可以使用驼峰命名,但不要用符号-,例如ylt-demo否则git中的配置文件命名也会带-,然后就会出现无法自动加载配置的bug.

3.集成Hystrix服务降级,熔断和dashboard(服务降级和服务监控可视化界面)

老套路:

第一步:引入依赖

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

引入可视化界面的依赖:

<dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-hystrix-dashboard</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>

第二步:启动类上添加注解:

这里特别注意,spring-cloud Finchley.SR1这个版本在图形化界面输入监控的服务地址后有bug,不能正常显示监控页面,所以启动类里配置了Bean,不知道什么时候能修复.然后关于注解,可以用@SpringCloudApplication来代替前面三个注解入下图,这样可以少写点注解...

springcloud仓储系统库存准确_微服务架构_04

且如果引用了Feign组件,其实@EnableCircuiBreaker也没必要添加,Fei中已内置CircuiBreaker断路器和Ribbon.

@SpringBootApplication
@EnableDiscoveryClient
@EnableCircuitBreaker
@EnableFeignClients
@EnableHystrixDashboard
public class YltOutpatientApplication {
    @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;
    }

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

第三步:在yml中添加配置:

feign:
  client:
    config:
      feignName:
        connectTimeout: 5000
        readTimeout: 2000
  hystrix:
    enabled: true

hystrix:
  command:
    default:
      execution:
        timeout:
          enabled: false
        isolation:
          thread:
            timeoutInMilliseconds: 2000

然后我们来在该服务中调其它服务,并且在其它服务中模拟调用场景,我传当入的id为偶数时,被调用服务的线程不休眠,直接返回success,当我传入的num为奇数时,被调用的服务休眠3秒,这样就可以模拟出服务降级和熔断了.

首先配置FeignClient,新建接口,然后在该接口上添加@FeignClient注解,并指明服务的name以及服务降级时fallback的类.(这里我采用内部类方式,当然你也可以写在外面)

@FeignClient(name = "ylt-diagnose",fallback = DiagnoseClient.DiagnoseFallBack.class)
public interface DiagnoseClient {
    @GetMapping("/diagnose")
    String getDiagnose();
    @Component
    static class DiagnoseFallBack implements DiagnoseClient{

        @Override
        public String getDiagnose() {
            return "服务被降级了";
        }
    }
}

然后我们新建一个controller来测试调用:

在需要启动服务降级的方法上添加@HystrixCommand,该注解里含有很多参数,用于配置服务降级的策略,具体的限于篇幅不多解释,可以去spring官网:https://spring.io 去了解.

@RestController
public class HystrixController {
    @Autowired
    private DiagnoseClient diagnoseClient;

    @RequestMapping("/hystrixDiagnose")
    @HystrixCommand(commandProperties = {
            @HystrixProperty(name = "circuitBreaker.enabled",value = "true"),
            @HystrixProperty(name = "circuitBreaker.requestVolumeThreshold",value = "10"),
            @HystrixProperty(name = "circuitBreaker.sleepWindowInMilliseconds",value = "10000"),
            @HystrixProperty(name = "circuitBreaker.errorThresholdPercentage",value = "60")
    })
    public String hystrixDiagnose(@RequestParam("num")Integer num) {
        if (num % 2 ==0){
            return "success";
        }
        return diagnoseClient.getDiagnose();
    }

    public String fb() {
        return "太拥挤了,请稍后再试";
    }

    public String fba() {
        return "fba:太拥挤了,请稍后再试";
    }

//    @RequestMapping("/fbahystrixDiagnose")
//    @HystrixCommand
//    public String fbahystrixDiagnose() {
//        return diagnoseClient.getDiagnose();
//    }
}

然后启动项目,打开浏览器进行测试:

首先我们输入Num为1:

springcloud仓储系统库存准确_微服务架构_05

输入Num为2:

springcloud仓储系统库存准确_高可用_06

OK,没问题,预期效果达成,下面我们来看看前面配的可视化监控界面:

springcloud仓储系统库存准确_微服务架构_07

界面很酷炫,一头豪猪... 该界面需要登录,分以下三种方式:

默认的集群监控:通过URL:http://turbine-hostname:port/turbine.stream开启,实现对默认集群的监控。

指定的集群监控:通过URL:http://turbine-hostname:port/turbine.stream?cluster=[clusterName]开启,实现对clusterName集群的监控。

单体应用的监控:通过URL:http://hystrix-app:port/hystrix.stream开启,实现对具体某个服务实例的监控。

演示一下第三种,以我刚刚打开的服务降级为例:

springcloud仓储系统库存准确_高可用_08

输入对应地址,然后点击Montior Stream 即可跳转到视图页面:

springcloud仓储系统库存准确_zipkin_09

页面长这样,然后我们测试一下看看:

springcloud仓储系统库存准确_微服务架构_10

当我不断的刷新num=1和num=2的页面时,监控界面会不断改变,不同颜色的字以及圆的大小等都有不同的含义,想了解更多参见spring官网.

4.集成服务追踪zipkin

第一步:引入依赖

<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-client</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <!--只有下面这两个依赖才是核心依赖-->
        <dependency>
            <groupId>io.zipkin.java</groupId>
            <artifactId>zipkin-autoconfigure-ui</artifactId>
            <version>2.11.2</version>
        </dependency>
        <dependency>
            <groupId>io.zipkin.java</groupId>
            <artifactId>zipkin-server</artifactId>
            <version>2.11.2</version>
        </dependency>

第二步:启动类添加注解

@SpringBootApplication
@EnableDiscoveryClient
@EnableZipkinServer
public class YltzipkinApplication {

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

第三步:yml配置(这里zipkin配置只需要指明服务发现中心地址和自己的端口号及application-name即可,主要说一下其他需要被追踪的微服务)

spring: 
  zipkin:
    base-url: http://localhost:9411/ #指明zipkin的地址.
    service:
      name: test #指明被追踪的服务名,尽量与项目名保持一致方便追踪.
    enabled: true
    sender:
      type: web #指明通过哪种方式发送服务的链路追踪,这里通过web方式,也可以采取mq,sql等方式.
  sleuth:
    sampler:
      probability: 1 #指明抽样比率,默认为0.1即10%,这里为了演示故调大为1更直观.

如此就完成了zipkin服务追踪,下面我们来看一看,启动zipkin并访问其地址:

springcloud仓储系统库存准确_spring cloud_11

可以在其中看到所有微服务之间的调用关系,以及每个服务之间的调用耗时,以及调用的具体接口等,这样就可以解决多服务之间调用,辅助问题排查,代码调优等...

5.引入多服务的监控组件turbine,这里啰嗦下为什么要引用turbine,因为在集群的情况下,服务之间的调用很可能是被负载到了不同的服务器上,使得相同的服务重复出现在监控页面,而我们有时往往需要一个可以把集群服务聚合在一起进行监控的工具,此时需要通过turbine来解决.

第一步:引入依赖

<dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</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-netflix-turbine-stream</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-stream-rabbit</artifactId>
        </dependency>

 

第二步:启动类上添加注解

@SpringBootApplication
@EnableDiscoveryClient
@EnableTurbineStream
public class TurbineApplication {

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

第三步:修改yml配置文件

spring:
  application:
    name: turbine

server:
  port: 8767
eureka:
  client:
    service-url:
      defaultZone: http://laohan:123@localhost:8761/eureka
turbine:
  combine-host-port: true #是否合并相同的服务

combine-host-port=true,可以让同一主机上的服务通过主机名与端口号的组合来进行区分,默认情况下会以host来区分不同的服务,这会使得在本机调试的时候,本机上的不同服务聚合成一个服务来统计。

访问一下来试试看(http://localhost:8767/turbine.stream,当你看到data里有内容时就说明成功了,当然如果data里一直处于Ping的状态时,你需要先测试几个调用feign的接口,然后再来观察,如果仍处于Ping,只能ping到一行数据,你可以坚持rabbitmq是否已经启动,防火墙是否关闭,端口是否已开启,配置是否正确等...:

springcloud仓储系统库存准确_微服务架构_12

有了该数据之后,你可以访问dashboard,将http://localhost:8767/turbine.stream输入并点击Monitor Stream 按钮便可进入监控页面了,进入监控页面后通过不断调用接口可以观察到服务调用的健康状况:

springcloud仓储系统库存准确_zipkin_13

springcloud仓储系统库存准确_zipkin_14

至此,spring-cloud常用的组件集成的差不多了,下一篇【spring-cloud】spring-cloud-从入门到高可用-下

将带领大家搭建高可用的集群配置方案以及注册中心加密等.还有一些不常用的组件,后续会陆续补上...