SpringCloud实践(二) 微服务核心组件Eureka

    本节进入SpringCoud的核心组件Eureka的学习实践。

    Spring Cloud Eureka是Spring Cloud Netflix 微服务套件中的一部分,基于Netflix Eureka做了二次封装,主要是负责完成微服务中服务治理功能。本节主要是通过实践来学习以下二点:

    构建微服务注册中心

    服务注册和服务发现demo

    在下一节,将学高可用的服务注册以及原理

    服务治理中最核心的就是服务注册和服务发现。在微服务的框架中,一般会有一个注册中心(注册中心也可以是集群,以防止注册中心崩溃,微服务的核心就是去中心化),每个服务想注册中心登记自己的服务,通过主机、端口、通信协议等信息通知注册中心,服务注册中心通过心跳的方式去监测清单中的服务是否可用,如果不可用则从服务清单中剔除。

    服务发现:服务调用不再通过具体的实例地址来调用,而是通过服务名发起请求,由注册中心依据配置的服务发现策略,如轮换、负载均衡等进行服务调用,实际框架为了性能等因素,不会每次都想注册中心获取服务的方式,会有一些实现策略来处理。

    一、实践环境说明

    1、IDEA环境

    2、jdk 1.7

    3、目标:构建1个注册中心,发布相同的2个服务,1个消费者。用户通过消费者来分别调用相同的2个服务。

二、搭建服务注册中心

    1、创建注册中,创建一个Spring Boot工程,与上节内容类似,取名为SpringCloudEurekaServer,


接着跟着向导进行如下设置



一路继续后完成,这个时候会自动下载springcloud依赖


在pom.xml 中引入必要的依赖:    

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>

	<groupId>com.study</groupId>
	<artifactId>springcloud</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<packaging>jar</packaging>

	<name>springcloud</name>
	<description>Demo project for Spring Boot</description>

	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>2.0.0.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>
		<spring-cloud.version>Finchley.M8</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>
			<snapshots>
				<enabled>false</enabled>
			</snapshots>
		</repository>
	</repositories>


</project>

2、添加EurekaServer启动入口

    在创建的工程中,找到SpringcloudApplication.java,加入注解@EnableEurekaServer,以确保启动这个应用的时候,启动服务注册中心服务:    

package com.study.springcloud;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;

@EnableEurekaServer
@SpringBootApplication
public class SpringcloudApplication {

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

	}
}

3、修改应用的配置文件:application.properties

server.port=11111

spring.application.name=Sping-Boot-Admin-Web
eureka.instance.hostname=localhost
#true表示将自己注册为一个服务,否则是启动了一个注册中心
eureka.client.register-with-eureka=false
eureka.client.fetch-registry=false
eureka.client.service-url.defaultZone=http://${eureka.instance.hostname}:${server.port}/eureka/ 
由于Eureka可以将自己作为一个服务,所以当作为注册中心使用的时候,需要将eureka.client.register-with-eureka=false 设置为false。 从上面的配置文件可以看到,注册中心的端口为本机: localhost 的11111 端口

4、启动注册中心

将工程启动起来后,通过下面URL访问: http://localhost:11111/


这里可以看到,当前的注册服务还全是空白。

三、创建一个服务

下面我们将创建一个服务应用,并向注册中心进行注册。

创建一个新Module,创建的过程与上面类似,在设置工程名称时,我们用 springcloudEurekaClient 来标识


工程结构也很简单,都是创建后自带的:


接着,我们编写一个服务,这个服务是一个REST风格的接口,还是用helloworld来做接口吧:

package com.study.springcloud;


import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.List;

@SpringBootApplication
@RestController
@EnableEurekaClient
public class SpringcloudApplication {

	@Autowired
	private DiscoveryClient discoveryClient;

	@Value("${server.port}")
	String port;


	@RequestMapping("/hello")
	String sayHello() {

		return "Hello World!" + "I am calling from "+"port:"+port;
	}

	@RequestMapping("/getRegistered")
	public String getRegistered(){
		String rst="";
		List<ServiceInstance> list = discoveryClient.getInstances("STORES");
		System.out.println( "discoveryClient.getInstances().size()=" + list.size());
		if (list != null && list.size() > 0 ) {
			System.out.println( list.get(0).getUri()  );
		}

		for( String s :  discoveryClient.getServices()){
			System.out.println("services " + s);
			List<ServiceInstance> serviceInstances =  discoveryClient.getInstances(s);
			for(ServiceInstance si : serviceInstances){

				rst=rst + "    services:" + s + ":getHost()=" + si.getHost()+":getPort()=" + si.getPort()+":getServiceId()=" + si.getServiceId()   ;
				System.out.println("    services:" + s + ":getHost()=" + si.getHost());
				System.out.println("    services:" + s + ":getPort()=" + si.getPort());
				System.out.println("    services:" + s + ":getServiceId()=" + si.getServiceId());
				System.out.println("    services:" + s + ":getUri()=" + si.getUri());
				System.out.println("    services:" + s + ":getMetadata()=" + si.getMetadata());
			}

		}


		return rst;
	}


	//这是一个注册客户端,向注册中心进行注册一个服务
	public static void main(String[] args) {
		SpringApplication.run(SpringcloudApplication.class, args);
	}
}

@RestController 和@EnableEurekaClient 注解,来引入这个应用是EurekaClient和Rest风格的接口,其他的注解也很容易懂,基本就是: 自动装配一个DiscoveryClient对象,对两个方法,sayHello和getRegistered 标注为请求映射。

比较重要的是看一下配置文件:application.properties

server.port=31111

eureka.instance.hostname=localhost
eureka.client.service-url.defaultZone=http://${eureka.instance.hostname}:11111/eureka/ 
# 重点需要注意spring.application.name,这个是在以后服务与服务之间相互调用是根据这个name
spring.application.name=service-sayHello 

第一个端口 31111,标识这个服务是 localhost 的31111端口, 

eureka.client.service-url.defaultZone=http://${eureka.instance.hostname}:11111/eureka/  表示注册中心为本机的11111端口

spring.application.name=service-sayHello  表示注册的服务为 service-sayHello 前面说过,微服务架构里面,发现服务是通过服务名来发现。

下面启动这个服务,在看看注册中心:


上面看到一个服务, service-sayhello 已经启动,并且绑定的端口是31111。 我们可以再注册一个同样的服务 绑定端口是21111,修改一下配置文件,再启动一次,这下看到 http://localhost:11111/ 已经注册了两个服务:


由于服务名都是 service-sayhello,所以Availability Zones里面体现有2个服务,Status 列出了对应的端口。

好了现在我们拥有了一个注册中心,相同服务名的2个应用也注册成功了,下一步,我们就是要创建服务的消费者来看如何调用服务。

四、服务消费者

服务消费者的工程也和前面类似,在输入Module名称的时候,输入 springcloudEurekaConsumer

下面,我们将创建一个http应用,这个应用输入URL,会调用到我们前面发布的2个服务。因为简单起见,我们将这个consuemr应用也注册为一个服务,只是这个服务是面向http调用。   用户---通过URL调用--consumer---调用服务SERVICE-SAYHELLO--

返回数据---consumer--返回给浏览器。工程如下图所示

HelloService.java 这个类是对外服务类,被controller 调用,被调用后将调用微服务 service-sayhello. 

package com.study.springcloud;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;

@Service
public class HelloService {

    @Autowired
    private RestTemplate restTemplate;

    public String  sayHello(){
        String str= restTemplate.getForObject("http://SERVICE-SAYHELLO/hello", String.class);
        return str;

    }

    public String  getRegistered(){
        String str= restTemplate.getForObject("http://SERVICE-SAYHELLO/getRegistered", String.class);
        return str;

    }
}

HelloController.java 这个类捕捉http请求,并调用HelloService的接口

package com.study.springcloud;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class HelloController {
    @Autowired
    private  HelloService helloService;

    @RequestMapping("/hi")
    public  String hello(){
        String str=  helloService.sayHello();
        System.out.println("call  hello service:"+str);
        return str;

    }

    @RequestMapping("/getRegistered")
    public  String getRegistered(){
        String str=  helloService.getRegistered();
        System.out.println("call  hello service:"+str);
        return str;

    }

}


当然,上面例子中,我们也可以不要HelloService,直接在helloController中自动装配RestTemplate。

SpringcloudApplication.java 这个类负责启动当前consumer服务,通过RestTemplate的Spring Bean实例,并通过@LoadBalanced标识来开启客户端负载均衡

package com.study.springcloud;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.context.annotation.Bean;
import org.springframework.web.client.RestTemplate;

@SpringBootApplication
@EnableDiscoveryClient  //向服务中心注册
public class SpringcloudApplication {

	public static void main(String[] args) {
		SpringApplication.run(SpringcloudApplication.class, args);
	}
	@Bean
	@LoadBalanced //表示下列方法开启负载均衡
	RestTemplate  restApiHello(){
		return new RestTemplate ();
	}

}

配置文件如下:

server.port=31311

eureka.client.service-url.defaultZone=http://localhost:11111/eureka/
# 重点需要注意spring.application.name,这个是在以后服务与服务之间相互调用是根据这个name
spring.application.name=service-consumer 

还有需要注意的是,因为用了负载均衡,在pom.xml中应该要引入ribbon组件:

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

	<groupId>com.study</groupId>
	<artifactId>springcloud</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<packaging>jar</packaging>

	<name>springcloud</name>
	<description>Demo project for Spring Boot</description>

	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>2.0.1.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>
		<spring-cloud.version>Finchley.M9</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>

		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.cloud</groupId>
			<artifactId>spring-cloud-starter-ribbon</artifactId>
		</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>
			<snapshots>
				<enabled>false</enabled>
			</snapshots>
		</repository>
	</repositories>


</project>
启动上面的consumer以后,


这个时候,我们调用consumer的http接口, http://localhost:31311/hi,会发现,由于采用了负载均衡方式,每次刷新,浏览器答应的端口都在变化,21111或者31111,说明每次调用的服务是不同的。同样,也可用通过http://localhost:31311/getRegistered来调用提供的另外一个接口,下图为调用http://localhost:31311/hi返回的情况:



猜你喜欢

转载自blog.csdn.net/csolo/article/details/80311063
今日推荐