Spring Cloud简述及简单入门实例

1 概述

Spring Cloud基于Spring Boot,提供了多个组件来帮助企业进行微服务系统建设;它提供的主要功能有:服务注册中心/服务注册/服务调用/负载均衡/断路器等;一般情况下它都是使用已有的开源软件,在其基础上按Spring Boot的理念来进行封装,简化各种个组件的调用以及各个组件之间的交互。

2 常用组件

Spring Cloud主要包含以下常用组件:

2.1 Eureka

分成两类,一是注册中心及EurekaServer,用于提供服务注册/服务申请等功能;
一是被注册者及服务提供者EurekaClient,用于向EurekaServer注册服务并可从EurekaServer获取需要调用的服务地址信息;
需要向外提供服务的应用,需要使用EurekaClient来向Server注册服务。

2.2 Ribbon

负责进行客户端负载均衡的组件;一般与RestTemplate结合,在访问EurekaClient提供的服务时进行负载均衡处理。
也就是说,Ribbon用于服务调用者向被调用者进行服务调用,并且如果服务者有多个节点时,会进行客户端的负载均衡处理;

2.3 Feign

与Ribbon功能类型,用于调用方与被调用方的服务调用,同时进行客户端负载均衡的处理;不过它能提供类似本地调用的方式调用远程的EurekaClient提供的服务;它实际上是在Ribbon基础上进行了进一步的封装来提高调用服务的简便性。

2.4 待补充

3 使用示例

3.1 示例场景

假设现在有SERVICEAPP1向外提供服务,该服务同时部署两个节点;Client通过Feign或者是Ribbon调用其提供的服务,其部署关系及数据流向图如下所示:
这里写图片描述

第一步:启动注册中心;服务提供者及调用者向服务中心注册;
第二步:服务调用者向服务中心申请服务,根据服务中心返回的地址信息调用服务提供者提供的服务;
第三步:注册中心通过心跳检测方式检测服务提供者的状态,当某个提供者连接不上时,发送消息通知所有注册者;

下面详细说明每个节点的实现; 具体代码见https://github.com/icarusliu/learnSpringCloud

3.2 Eureka服务端

详细代码见GITHUB工程中名为EurekaServer的模块;
a. MVN依赖引入
引入EurekaServer相关的依赖包

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

b. Spring Boot注解引入及服务启动类
主要是通过EnableEurekaServer来表明节点是服务端。

@SpringBootApplication
@EnableEurekaServer
public class EurekaServerApplication {

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

c. Application.yml配置
主要是配置端口及配置是否向服务中心注册及向服务中心获取注册信息;
这两项配置均需配置成false; 单纯的做服务器的节点不需要再向服务器注册及申请注册信息。

server:
  port: 8761

eureka:
  instance:
    hostname: localhost
  client:
    # 以下两项一定要是false,表明自己是服务器,而不需要从其他主机发现服务
    registerWithEureka: false
    fetchRegistry: false
    serviceUrl:
      defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
  server:
    waitTimeInMsWhenSyncEmpty: 0

启动完后通过http://localhost:8761/可以看到EurekaServer的监控页面; 此时还没有节点注册;

3.3 服务提供方

详细代码见GITHUB工程中名为ServiceApp1的模块;
a. MVN依赖引入
需要引入EurekaClient相关的依赖包;

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

b. 注解引入
通过EnableEurekaClient注册表明其是EurekaClient;
该注解会使得应用向EurekaServer进行注册,同时获取EurekaServer上所注册的所有应用信息; 这些注册信息通过自动注入的EurekaClient类可以获取到。

@SpringBootApplication
@EnableEurekaClient
@RestController
public class ServiceApp1Application {
    @Autowired
    private EurekaClient client;

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

    @RequestMapping("/info")
    public String test() {
        return "test";
    }

    @RequestMapping("/test")
    public String testClient() {
        InstanceInfo instanceInfo = client.getNextServerFromEureka("SERVICEAPP1", false);
        return instanceInfo.getHomePageUrl();
    }
}

c. application.yml配置
配置端口/EurekaServer地址信息以及本服务名称;
同一应用可部署在多个节点时,或者在本地通过修改此处的端口可启动多次ServiceApp1Application 中的main方法; 此时会启动多个提供同一服务的服务节点; 如果启动多次时spring.application.name是不一样的,那么EurekaServer将会将它们当成是不同的服务;

server:
  port: 8080
eureka:
  client:
    serviceUrl:
      defaultZone: http://localhost:8761/eureka/
spring:
  application:
    name: serviceApp1

启动main函数后,通过http://localhost:8761可以看到已经有节点注册;
如果要启动多个节点做负载均衡,可以修改application.yml中的端口信息后再次启动main函数就可以了。

3.4 服务调用方

3.4.1 通过EurekaClient调用

详细代码见GITHUB工程中名为ServiceApp1的模块;
通过EnableEurekaClient进行服务注册的服务提供方的应用中,也可以通过注入EurekaClient的方式来调用其它服务提供方注册的服务,其使用方式如下:

@RestController
public class TestController {
    @Autowired
    private EurekaClient client;

    @RequestMapping("/test")
    public String testClient() {
        InstanceInfo instanceInfo = client.getNextServerFromEureka("SERVICEAPP1", false);
        return instanceInfo.getHomePageUrl();
    }
}

3.4.2 通过Ribbon调用服务

Ribbon是一个客户端负载均衡器;如Redware等均是服务器端的负载均衡器;而Ribbon的负载均衡发生在客户端上。
详细代码见RibbonClient模块;
a. 依赖

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-eureka</artifactId>
    <version>1.3.0.RC1</version>
</dependency>

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

b. 注解引入及RestTemplate注入
主要是通过EnableDiscoveryClient注解来向EurekaServer注册/获取注册信息; 此时可以通过RestTemplate来调用远程服务。

@SpringBootApplication
@EnableDiscoveryClient
public class RibbonClientApplication {

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

    @Bean
    @LoadBalanced
    RestTemplate restTemplate() {
        return new RestTemplate();
    }

}

c. application.yml配置

server:
  port: 8091
eureka:
  client:
    serviceUrl:
      defaultZone: http://localhost:8761/eureka/
spring:
  application:
    name: ribbon-client

d. 服务调用

@RestController
public class TestController {
    @Autowired
    private RestTemplate restTemplate;

    @RequestMapping("/test")
    public String test() {
        return restTemplate.getForEntity("http://SERVICEAPP1/test", String.class).getBody();
    }
}

启动main函数后通过EurekaServer的监控页面可以看到有两个节点已经注册到EurekaServer上了。
这里会有点奇怪,此处主要是通过Ribbon来调用其它节点提供的服务,这个客户端应用为什么要注册到EurekaServer中去?
它主要是基于以下考虑:客户端应用向EurekaServer注册后,它就可以获取EurekaServer上的所有注册服务信息,并将其缓存到本地; 这样就不需要每次调用某个节点的服务都先去EurekaServer上获取该服务的地址信息; 而且注册还有个好处,假设某个服务提供者挂掉了,而新启动了一个新IP端口的节点提供同时的服务,那么EurekaServer可以通知所有注册的节点说集群中出现了这样变化,请更新本地缓存;因此客户端应用就可以去新的服务器调用服务, 而不是去已经挂掉的节点调用服务。
通过http://localhost:8091/test可以看到执行结果。

3.4.3 通过Feign调用服务

Feign与Ribbon功能类似,它们的不同只是Feign在调用上进行了进一步的封装。
a. 依赖

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-eureka</artifactId>
    <version>2.0.0.M2</version>
</dependency>

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-feign</artifactId>
    <version>2.0.0.M2</version>
</dependency>

b. 启动服务
相对Ribbon来说多了EnableFeignClients注解。

@SpringBootApplication
@EnableDiscoveryClient
@EnableFeignClients
public class FeignClientApplication {

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

}

c. RemoteClient定义
通过Feign调用服务时,主要是利用FeignClient注解指定需要调用的服务在哪类节点上;这个注解作用在接口上; 与RequestMapping结合使用,这样就可以指定接口的某个方法调用的是远程服务器的哪个接口。

@FeignClient("SERVICEAPP1")
public interface RemoteClient {
    @RequestMapping(method = RequestMethod.GET, value = "/test")
    String test();
}

d. application.yml配置

server:
  port: 8093
eureka:
  client:
    serviceUrl:
      defaultZone: http://localhost:8761/eureka/
spring:
  application:
    name: feign-client
feign:
  hystrix:
    enabled: true

e. 服务调用
通过预先定义好的RemoteClient类来调用远程服务器的相应接口;

@RestController
public class TestController {
    @Autowired
    private RemoteClient remoteClient;

    @RequestMapping("/test")
    public String test() {
        return remoteClient.test();
    }
}

启动main方法后,通过EurekaServer的监控页面可以看到又多了一个名称为FEIGN-CLIENT的节点; 为什么会多这个节点与Ribbon方式访问时多一个注册节点的原因是一样的。
此时可以通过http://localhost:8093/test来查看执行结果。

问题

1 使用Feign来调用服务时,报以下错误:com.netflix.client.ClientException: Load balancer does not have available server for client
原因是手贱将spring-cloud-starter-eureka的依赖给去掉了,添加上去即可:

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-eureka</artifactId>
    <version>2.0.0.M2</version>
</dependency>

否则Feign客户端将无法注册到Eureka服务器并且调用其它节点提供的服务。

猜你喜欢

转载自blog.csdn.net/icarusliu/article/details/79461012