Spring Cloud初见(三)

    服务治理可以说是为服务架构中最为核心和基础的模块了,它主要用来实现各个微服务实例的自动化注册与发现。

    在最开始构建微服务的时候可能服务不多,我们可以通过做一些静态配置来完成服务的调用。比如,有两个服务A和B,其中A需要调用服务B来完成一个业务操作时,为了实现服务B的高可用,不论是采用服务端负载还是客户端负载,都需要手动取维护B的具体实例名单。但是随着业务规模的壮大,我们的静态配置就会越来越难以维护。为了解决微服务架构中服务实例维护问题,产生了大量的服务治理框架和产品。这些框架和产品都围绕这服务注册与服务发现机制来完成对微服务应用实例的自动化管理。

 

  •   服务注册:在服务治理框架中,通常会构建一个注册中心,每个服务单元向注册中心登记自己提供的服务,将主机与端口号,版本号,通信协议等一些附加信息告知注册中心,注册中心按照服务名分类组织服务清单。比如,我么有两个提供服务A分别运行于192.1680.100:8000和192.168.0.101:8000,另外还有三个提供服务B的进程分别运行于192.168.0.100:9000,192.168.0.101:9000,192.168.0.102:9000位置上。当这些进程均启动,并想注册中心注册自己的服务后,注册中心就会维护类似下面的一个服务清单。另外,服务注册中心还需要以心跳的方式去检测清单中的服务是否可用,若不可用需要从服务清单中剔除,达到排除故障服务的效果。
服务名位置
服务A192.168.0.100:8000,192.168.0.101:8000
服务B192.168.0.100:9000,192.168.0.101:9000,192.168.0.102:9000

 

  •  服务发现:由于在服务治理框架下运作,服务间的调用不再通过制定具体的实例地址来实现,而是通过向服务名发起请求调用实现。所以,服务调用方在调用服务提供方接口的时候,并不知道具体的服务实例位置。因此,调用方需要向服务注册中心咨询服务,并获取所有服务的实例清单,以实现对具体服务实例的访问。比如,现有服务C希望调用服务A,服务C就需要向注册中心发起咨询服务请求,服务注册中心就会将服务A的位置清单返回给服务C,如按上面服务A的情况,C便会回去服务A的两个可用地址192.168.0.100:8000和192.168.0.101:8000。当服务C要发起调用的时候,便从该清单中以某种轮循策略取出一个位置来进行服务调用,这就是后续将会介绍的客户端负载均衡。这里我们只是列举了一种简单的服务治理逻辑。实际的框架为了性能的因素,不会采用每次都向服务注册中心获取服务的方式,并且不同的应用场景在缓存服务剔除等机制上也会有不一样的策略。

 

Netflix Eureka

Spring Cloud Eureka,使用Netflix Eureka来实现服务注册与发现,它既包含了服务端组件,又包含了客户端组件,并且服务端与客户端均采用Java编写,所以Eureka主要适用于通过Java实现的分布式系统,或是JVM兼容语言构建的系统。但是,由于Eureka服务端的服务治理机制提供了完备的RESTful API,所以它也支持跨平台。

    Eureka服务端,我们也称为服务注册中心。它同其他服务注册中心一样,支持高可用配置。依托于强一致性提供良好的服务实例可用性,可以应对多种不同的故障场景。如果Eureka以集群模式部署,当集群中有分片出现故障时,那么Eureka就会转入组我保护模式。它允许在分片故障期间继续提供服务的发现与注册,当故障分片回复运行时,集群中的其他分片会把他们的状态再次同步回来。

    Eureka客户端,主要处理服务的注册与发现。客户端服务通过注解和参数配置的方式,嵌入在客户端应用程序的代码中,在应用程序运行时,Eureka客户端向注册中心注册自身提供的服务并周期性地发送心跳来更新它的服务租约。同时,它也能从服务端查询当前注册的服务信息并把它们缓存到本地并周期性刷新服务状态。

    搭建服务注册中心

    首先创建一个基础的Spring Boot工程,并在pom.xml中引入必要的依赖内容,如下

    <properties>
		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
		<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
		<java.version>1.8</java.version>
		<spring-cloud.version>Finchley.RELEASE</spring-cloud.version>
	</properties>

	<dependencies>
		<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>

         通过@EnableEurekaServer注解启动一个服务注册中心提供给其他应用进行对话。只需要在启动类中添加该注解即可开启此功能

@EnableEurekaServer
@SpringBootApplication
public class EurekaServerApplication {

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

在默认设置下,该服务注册中心也会将自己作为客户端来尝试注册它自己,所以我们需要禁用它的客户端注册行为,只需要在application.properties中增加配置

server.port=1111
eureka.instance.hostname=localhost
eureka.client.register-with-eureka=false
eureka.client.fetch-registry=false
eureka.client.serviSSceUrl.defualtZone=http://${eureka.instance.hostname}:${server.port}/eureka/
  • eureka.client.register-with-eureka:该注册中心不向注册中心注册自己,所以设置为false,否则注册中心默认会注册自己
  • eureka.client.fetch-registry:该注册中心不需要检索服务,所以设置为false

完成配置后,启动应用并访问http://localhost:1111/。可以看到如下图,其中Instances currently registered with Eureka一栏是空的,说明该注册中心还没有注册任何服务

注册服务提供者

完成了服务注册中心的搭建之后,接下来尝试将一个Spring Boot应用加入Eureka的服务治理体系之中。首先创建一个Spring Boot基础项目,修改pom.xml,增加Spring Cloud Eureka模块的依赖,具体代码如下所示:

<dependencies>
		<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>

		<dependency>
			<groupId>org.springframework.cloud</groupId>
			<artifactId>spring-cloud-starter-eureka</artifactId>
			<version>1.4.4.RELEASE</version>
		</dependency>
	</dependencies>

	<dependencyManagement>
		<dependencies>
			<dependency>
				<groupId>org.springframework.cloud</groupId>
				<artifactId>spring-cloud-dependencies</artifactId>
				<version>RELEASE</version>
				<type>pom</type>
				<scope>import</scope>
			</dependency>
		</dependencies>
	</dependencyManagement>

接着,新建controller,注入DiscoveryClient对象,在日志中打印出服务的相关内容。需要注意的是getLocalServiceInstance()方法被标注为不推荐使用,因此想要获取本服务的server_id可以参照如下方法获取

@RestController
public class HelloController {

    private final Logger logger = Logger.getLogger(String.valueOf(getClass()));

    @Autowired
    private DiscoveryClient client;

    @RequestMapping(value="/hello",method = RequestMethod.GET)
    public String index(){
//        getLocalServiceInstance()方法不推荐使用,因此推荐下面这个方法
//        ServiceInstance instance = client.getLocalServiceInstance();
//        logger.info("/hello,host:"+instance.getHost()+", service_id:"+instance.getServiceId());
//        return "Hello World";
        client.getServices().forEach(id -> {
            client.getInstances(id).forEach(instance -> {
                logger.info("/hello, host:" + instance.getHost() + ", service_id:" + instance.getServiceId());
            });
        });
        return "Hello World";
    }
}

之后在启动类中通过@EnableDiscoveryClient注解声明服务提供者,激活Eureka中的DiscoveryClient实现。

@EnableDiscoveryClient
@SpringBootApplication
public class ServerClientApplication {

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

最后我们在application.properties配置文件中,通过spring.application.name属性来为服务命名。再通过eureka.client.serviceUrl.defaultZone来指定服务注册中心地址。配置如下

spring.application.name=hello-service

eureka.client.serviceUrl.defaultZone=http://localhost:1111/eureka/

启动服务后,可以发现,服务注册中心的控制台可以看到如下输出,名为hello-service的服务已经被注册成功了。在服务中心的控制台中,也可以看到输出: Registered instance HELLO-SERVICE/192.168.0.111:hello-service with status UP (replication=false)

通过访问http://localhost:8080/hello,直接向该服务发起请求,在控制台中可以看到如下输出:/hello, host:192.168.0.111, service_id:HELLO-SERVICE。这些输出内容就是在helloConttoller中注入的DiscoverClient接口对象,从服务注册中心获取的服务相关信息。

高可用注册中心

在微服务架构的分布式环境中,我们需要充分考虑发生故障的情况,所以在整个生产环境中必须对各个组件进行高可用部署,对于微服务如此,对于服务注册中心也是一样。因此我们需要构建高可用的服务注册中心以增强系统的可用性。

Eureka Server的设计一开始就考虑了高可用问题,在Eureka的服务治理设计中,所有节点即是服务提供方,也是服务消费方,服务注册中心也不例外。之前我们通过下面两个参数,让服务注册中心不再注册自己:

eureka.client.register-with-eureka=false
eureka.client.fetch-registry=false

Eureka Server 的高可用实际上就是将自己作为服务向其他服务注册中心注册自己,这样就可以形成一组互相注册的服务注册中心,以实现服务清单的相互同步,达到高可用的效果。

  • 创建application-peer1.properties,作为peer1服务中心的配置,并将serviceUrl指向peer2:
spring.application.name=eureka-server
server.port=1111

eureka.instance.hostname=peer1
eureka.client.serviceUrl.defaultZone=http://peer2:1112/eureka/
  • 创建application-peer2.propeeties,作为peer2服务中心的配置,并将serviceUrl指向peer1:

spring.application.name=eureka-server
server.port=1112

eureka.instance.hostname=peer2
eureka.client.serviceUrl.defaultZone=http://peer1:1111/eureka/

 

  • 修改hosts,将peer1和peer2都指向127.0.0.1
  • 通过mvn install打包成jar,之后通过java -jar demo-0.0.1-SNAPSHOT.jar --spring.profiles.active=peer2命令指定注册中心以peer2配置文件运行,并以同样的方式运行peer1服务注册中心,之后访问http://localhost:1111/,看到如下,可以看到peer2服务注册中心将两个都注册了。

 

  • 在设置了多节点的服务注册中心后,服务提供方还需要做一些简单的配置才能将服务注册到Eureka Server集群中。修改application.proerties,将注册中心指向peer1和peer2,中间用逗号隔开。
spring.application.name=hello-service

eureka.client.serviceUrl.defaultZone=http://localhost:1111/eureka/,http://localhost:1112/eureka/

下面启动该服务,可以看到两个服务注册中心都有该服务,此时关掉peer1,在peer2上其他服务依然能访问到hello-service,从而实现服务注册中心的高可用。

服务发现与消费

通过上述实验我们已经构建了微服务架构中的核心组件--服务注册中心(单节点模式和高可用模式)。经过简单的配置,使hello-service注册到Eureka注册中心上,称为该服务治理体系下的一个服务,现在我们已经有了服务注册中心服务提供者,那么下面我们来尝试构建服务消费者,它主要完成两个目标发现服务以及消费服务,其中服务发现的任务由Eureka的客户端完成,而服务消费的任务由Ribbon完成。Ribbon是一个服务端列表取轮巡访问以达到负载均衡的作用。当Ribbon与Eureka联合使用时,Ribbon的服务实例清单RibbonServerList会被DiscoveryEnabledNIWSServerList重写,扩展成从Eureka注册中心获取服务端列表。同时它也会用NIWSDiscoveryPing来取代IPing,它将职责委托给Eureka来确定服务段是否已经启动。

  • 首先,依照前面的示例,启动服务注册中心eureka-server以及hello-service服务,为了实验Ribbon的客户端负载均衡功能,我们启动两个不同端口的hello-service,端口分别为8081和8082,注册中心如下图

 

  • 接下来,创建一个Spring Boot基础工程来实现服务消费者,依赖如下,较之前的hello-service,新增了Ribbon模块的依赖spring-cloud-starter-ribbon。

 

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

		<dependency>
			<groupId>org.springframework.cloud</groupId>
			<artifactId>spring-cloud-starter-eureka</artifactId>
			<version>1.4.4.RELEASE</version>
		</dependency>

		<dependency>
			<groupId>org.springframework.cloud</groupId>
			<artifactId>spring-cloud-starter-ribbon</artifactId>
			<version>1.4.4.RELEASE</version>
		</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>RELEASE</version>
				<type>pom</type>
				<scope>import</scope>
			</dependency>
		</dependencies>
	</dependencyManagement>
  •  在启动类中通过@EnableDiscoveryClient注解让该应用注册为Eureka客户端应用,来获得服务发现的能力。同时,在该启动类中创建RestTemplate的Spring Bean实例,并通过@LoadBalanced注解开启客户端负载均衡。
@EnableDiscoveryClient
@SpringBootApplication
public class RibbonConsumerApplication {

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

	public static void main(String[] args) {
		SpringApplication.run(RibbonConsumerApplication.class, args);
	}
}
  • 创建ConsumerController类并实现/ribbon-consumer接口。在该接口中,通过在上面创建的RestTemplate来实现对HELLO-SERVICE接口的调用,可以看到这里访问的地址是服务名HELLO-SERVICE,而不是一个具体的地址。
@RestController
public class ConsumerController {
    @Autowired
    RestTemplate restTemplate;

    @RequestMapping(value="/ribbon-consumer", method= RequestMethod.GET)
    public String helloConsumer(){
        return restTemplate.getForEntity("http://HELLO-SERVICE/hello",String.class).getBody();
    }
}
  • 配置application.properties后,启动应用并通过http://localhost:9000/ribbon-consumer发起GET请求,成功返回了“HELLO World”。此时,我们可以看到Hello-service中两个应用交替输出日志,实现了负载均衡。

 

以上就是简单的服务注册与发现示例,构建了Eureka服务治理体系中的三个核心角色:服务注册中心,服务提供者以及服务消费者。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值