第三章 Spring CLoud Eureka 服务

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

开源:大家可以对实现一探究竟,甚至修改源码。

可靠:经过Netflix多年的生产环境考验,使用应该比较靠谱省心

功能齐全:不但提供了完整的注册发现服务,还有Ribbon等可以配合使用的服务。

基于Java:对于Java程序员来说,使用起来,心里比较有底。

spring cloud可以使用Spring Cloud, 与Eureka进行了很好的集成,使用起来非常方便。

当调用API或者发起网络通信的时候,无论如何我们都要知道被调用方的IP和服务端口,大部分情况是通过域名和服务端口,事实上基于DNS的服务发现,因为DNS缓存、无法自治和其他不利因素的存在,有很多局限。传统的DNS方式,都是通过nginx或者其他代理软件来实现,物理机器的ip和port都是固定的,那么nginx中配置的服务ip和port也是固定的,服务列表的更新只能通过手动来做,但如果后端服务很多时,手动更新容易出错,效率也很低,这在后端服务发生故障时,不可用时间就可能会加长。在微服务中,尤其是使用了Docker等虚拟化技术的微服务,其IP和port都是动态分配的,服务实例数也是动态变化的,那么就需要精细而准确的服务发现机制。当微服务app启动后,告诉其他服务自己的ip和端口,这里的其他服务就是Eureka Server和Eureka Client,这样其他服务就知道这个服务有多少实例在线,都在哪些地方,方便去负载均衡和调用。

Eureka属于客户端发现模式,客户端负责决定相应服务实例的网络位置,并且对请求实现负载均衡。客户端从一个服务注册服务中查询所有可用服务实例的库,并缓存到本地。服务调用时,客户端使用负载均衡算法从多个后端服务实例中选择出一个,然后发出请求。Eureka分为Eureka Server和Eureka client, Eureka Server是一个服务注册中心,为服务实例注册管理和查询可用实例提供了REST API,并可以用其定位、负载均衡、故障恢复后端服务的中间层服务。在服务启动后,EurekaClient向服务注册中心注册服务同时会拉去注册中心注册表副本;在服务停止的时候,Eureka Client向服务注册中心注销服务;服务注册后,Eureka Client会定时的发送心跳来刷新服务的最新状态。

客户端发现模式的优点是服务调用、负载均衡不需要和Eureka Server通信,直接使用本地注册表副本,因此Eureka Server不可用时是不会影响正常的服务调用,性能也不会因为网络延迟和服务端延迟受到影响。但其缺点也很明显,但某个服务不可用时,各个Eureka Client不能及时的知道,需要1~3个心跳周期才能感知,但是,由于基于Netflix的服务调用端都会使用Hystrix来容错和降级,当服务调用不可用时Hystrix也能及时感知到,通过熔断机制来降级服务调用,因此弥补了基于客户端服务发现的时效性的缺点。

EurekaServer采用的是对等通信(P2P),无中心化的架构,无master/slave区分,每一个server都是对等的,既是Server又是Client,所以其集群方式可以自由发挥,可以各点互连,也可以接力互连。Eureka Server通过运行多个实例以及彼此之间互相注册来提高可用性,每个节点需要添加一个或多个有效的serviceUrl指向另一个节点。利用Eureka Server这种架构特性,我在Eureka Server Cluster的部署时采用了三角形通信模型,三角形是一个很好的均衡模型,既是各点互连,又是接力互连,三角形本身就是一个稳定性几何形状,有着稳固、坚定搜索、耐压的特点,家具、建筑、交通等各种行业都有应用。如下图所示,Eureka Cluster的每个实例都和另外2个实例通信交互。

由于前面的两章是为没有看过SpringBoot的同学们写,不然不太理解为什么这么写,现在正是写SpringCLoud的流程。

Spring Cloud实现服务注册及发现:

所有的服务端及访问服务的客户端都需要连接到注册管理器(eureka服务器)。服务在启动时会自动注册自己到eureka服务器,每一个服务都有一个名字,这个名字会被注册到eureka服务器。使用服务的一方只需要使用该名字加上方法名就可以调用到服务。

第一步:首先创建一个Spring Cloud Eureka 服务注册中心,打开Eclipse,右键鼠标点击New,然后选择Other,选择Spring boot ----->Spring StarterProject ,WEB和Cloud Discovery中的EurekaServer即可,如下:


 


EurekaApplication

package com.example.demo;

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

@SpringBootApplication  //此注解表示开启程序入口
@EnableEurekaServer //此注解表示开启Eureka Server 
public class SpringCloudEurekaServerApplication {

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

application.yml配置

security:
  basic:
    enabled: true     #开启认证 是否开启基本的鉴权,默认为true
  user:
    name: user  #登录eureka  server中需要的账号
    password: 123456  #登录eureka  server中需要的密码
server:
  port: 8761    #eurekaserver中注册服务端口号
eureka:
  instance:
    hostname: localhost   #配置主机名
  client:
    register-with-eureka: false   #配置服务注册中心是否以自己为客户端进行注册(配置false)
    fetch-registry: false   #是否取得注册信息(配置false)
    service-url:
      defaultZone: http://${security.user.name}:${security.user.password}@${eureka.instance.hostname}:${server.port}/eureka
       #eureka服务地址; 配置eureka客户端的缺省域(该配置可能没有提示,请复制或者手动输入,切勿使用有提示的service-url会引起内置tomcat报错)
       # ${}是引用ER表达式节点取值    

pom.xml配置

<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.example.demo</groupId>
	<artifactId>spring-cloud-eureka</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<packaging>war</packaging>

	<name>spring-cloud-eurekaa</name>
	<description>spring-cloud-eureka</description>

	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>1.4.1.RELEASE</version>
	</parent>

	<properties>
		<!-- 文件拷贝时的编码 -->  
		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
		<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
		  <!-- 编译时的编码 -->  
        <maven.compiler.encoding>UTF-8</maven.compiler.encoding>  
        <!-- jdk版本 -->  
		<java.version>1.8</java.version>
	</properties>

	<dependencies>
			<!-- 注册中心 -->
		<dependency>
			<groupId>org.springframework.cloud</groupId>
			<artifactId>spring-cloud-starter-eureka-server</artifactId>
		</dependency>
		<!-- 用于注册中心访问账号认证 -->
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-security</artifactId>
		</dependency>
		<!-- 用于热部署 -->
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-devtools</artifactId>
		</dependency>		
	</dependencies>
	<dependencyManagement>
		<dependencies>
		<!-- 版本依赖管理,故之后添加依赖无需指定version -->
			<dependency>
				<groupId>org.springframework.cloud</groupId>
				<artifactId>spring-cloud-dependencies</artifactId>
				<version>Camden.SR2</version>
				<type>pom</type>
				<scope>import</scope>
			</dependency>
		</dependencies>
	</dependencyManagement>

	<build>
		<plugins>
			<!-- 用以为integration-test提供支持。 -->
			<plugin>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
			</plugin>
		</plugins>
	</build>
</project>

启动浏览器访问8761:





创建一个用户微服务

项目结构:



SpringCloudUserApplication类

package com.example.demo;

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

@SpringBootApplication	//此注解表示开启程序入口
@EnableEurekaClient	//此注解表示开启Eureka Client 
public class SpringCloudUserApplication {

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

UserController类

package com.example.demo.controller;

import java.util.ArrayList;
import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;

import com.example.demo.entity.User;
import com.example.demo.repository.UserRepository;
import com.google.common.collect.Lists;
import com.netflix.appinfo.InstanceInfo;
import com.netflix.discovery.EurekaClient;

@RestController
public class UserController {

	@Autowired
	private UserRepository userRepository;

	@Autowired
	private EurekaClient eurekaClient;

	@Autowired
	private DiscoveryClient discoveryClient;

	@GetMapping("/user/{id}")
	public User findById(@PathVariable Long id) {
		return this.userRepository.findOne(id);
	}
	/**
	 * 获取项目IP和端口号
	 * @return
	 */
	@GetMapping("/serviceUrl")
	public String serviceUrl() {
		InstanceInfo instance = this.eurekaClient.getNextServerFromEureka("SPRING-CLOUD-USER", false);
		return instance.getHomePageUrl();
	}
	
	/**
	 * 以JSON形式返回:获取主机IP、端口号和yml里metadata数据
	 * @return
	 */
	@GetMapping("/showInfo")
	public ServiceInstance showInfo() {
		ServiceInstance localServiceInstance = this.discoveryClient.getLocalServiceInstance();
		return localServiceInstance;
	}
	
	// 该请求不会成功
	@PostMapping("/postUser")
	public User postUser(@RequestBody User user) {
		return user;
	}

	
	@GetMapping("/getUser")
	public User getUser(User user) {
		return user;
	}

	@GetMapping("list")
	public List<User> list() {
		ArrayList<User> list = Lists.newArrayList();
		User user = new User(1L, "zhangsan");
		User user2 = new User(2L, "zhangsan");
		User user3 = new User(3L, "zhangsan");
		list.add(user);
		list.add(user2);
		list.add(user3);
		return list;

	}
}


User类

package com.example.demo.entity;

import java.math.BigDecimal;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;

@Entity
public class User {

  public User(Long id, String username) {
    super();
    this.id = id;
    this.username = username;
  }

  public User() {
    super();
  }

  @Id
  @GeneratedValue(strategy = GenerationType.AUTO)
  private Long id;

  @Column
  private String username;

  @Column
  private String name;

  @Column
  private Short age;

  @Column
  private BigDecimal balance;

  public Long getId() {
    return this.id;
  }

  public void setId(Long id) {
    this.id = id;
  }

  public String getUsername() {
    return this.username;
  }

  public void setUsername(String username) {
    this.username = username;
  }

  public String getName() {
    return this.name;
  }

  public void setName(String name) {
    this.name = name;
  }

  public Short getAge() {
    return this.age;
  }

  public void setAge(Short age) {
    this.age = age;
  }

  public BigDecimal getBalance() {
    return this.balance;
  }

  public void setBalance(BigDecimal balance) {
    this.balance = balance;
  }
}

UserRepository类

package com.example.demo.repository;

import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

import com.example.demo.entity.User;

/**
 * 一个DAO数据访问层,继承JpaRepository
 *
 */
@Repository
public interface UserRepository extends JpaRepository<User, Long> {

}


application.yml配置

server:
  port: 7900  #服务端口号
spring:
  jpa:
    generate-ddl: false   #启动的时候是否创建ddl语句(已经创建了sql语句,故为false)
    show-sql: true         #打印sql语句
    hibernate:
      ddl-auto: none        #由于jpa是依赖于hibernate,启动的时候不做ddl语句的处理
  datasource:
    platform: h2      #数据源,配置是使用h2数据库
    schema: classpath:schema.sql    #sql创建语句
    data: classpath:data.sql        #插入数据库的信息
  application:
    name: spring-cloud-user #微服务的名称,建议小写

#日志传输出级别
logging:
  level:
    root: INFO
    org.hibernate: INFO
    org.hibernate.type.descriptor.sql.BasicBinder: TRACE #打印sql以及参数
    org.hibernate.type.descriptor.sql.BasicExtractor: TRACE #打印sql以及参数
    com.example: DEBUG
    
#eureka服务注册配置
eureka:
  client:
    healthcheck:
      enabled: true # 开启健康检查
    serviceUrl:
      defaultZone: http://user:123456@localhost:8761/eureka     #eureka注册服务的URL
  instance: 
    prefer-ip-address: true  #显示主机ip
    instance-id: ${spring.application.name}:${spring.cloud.client.ipAddress}:${spring.application.instance_id:${server.port}} #主机名:应用名:应用端口
    metadata-map:
      zone: DE      # eureka可以理解的元数据
      yuanzhenhua: AE  # 不会影响客户端行为
    lease-renewal-interval-in-seconds: 5 #续约到期时间(默认90秒)
#lease-renewal-interval-in-seconds 作为实例还涉及到与注册中心的周期性心跳,默认持续时间为30秒(通过serviceUrl)。在实例、服务器、客户端都在本地缓存中具有相同的元数据之前,服务不可用于客户端发现(所以可能需要3次心跳)。你可以使用eureka.instance.leaseRenewalIntervalInSeconds 配置,这将加快客户端连接到其他服务的过程。在生产中,最好坚持使用默认值,因为在服务器内部有一些计算,他们对续约做出假设
#可以通过eureka.instance.metadataMap修改元数据,这些元数据不会改变客户端的行为。 默认情况下一个eureka服务使用主机名称注册,那么只能一个主机一个服务。通过eureka.instance.metadataMap.instanceId你可以修改这个实例ID。

data.sql

insert into user(id,username, name, age, balance) values(1,'zhangsan', '张三', 18, 10.00);
insert into user(id,username, name, age, balance) values(2,'lisi', '李四', 19, 20.00);
insert into user(id,username, name, age, balance) values(3,'wangwu', '王五', 20, 30.00);
insert into user(id,username, name, age, balance) values(4,'malu', '马六', 21, 34.00);

schema.sql

drop table user if exists;
create table user(
	id bigint generated by default as identity,
	username varchar(40),
	name varchar(20),
	age int(3),
	balance decimal(10,2), 
	primary key(id)
);

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.example.demo</groupId>
	<artifactId>spring-cloud-user</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<packaging>war</packaging>

	<name>spring-cloud-user</name>
	<description>spring-cloud-user</description>

	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>1.4.1.RELEASE</version>
	</parent>

	<properties>
		<!-- 文件拷贝时的编码 -->  
		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
		<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
		  <!-- 编译时的编码 -->  
        <maven.compiler.encoding>UTF-8</maven.compiler.encoding>  
        <!-- jdk版本 -->  
		<java.version>1.8</java.version>
	</properties>

	<dependencies>
		<!-- 添加JPA的依赖 -->
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-data-jpa</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>
		<!-- 添加h2数据库的依赖 -->
		<dependency>
			<groupId>com.h2database</groupId>
			<artifactId>h2</artifactId>
			<scope>runtime</scope>
		</dependency>
		<!-- 注册中心 -->
		<dependency>
			<groupId>org.springframework.cloud</groupId>
			<artifactId>spring-cloud-starter-eureka</artifactId>
		</dependency>
		<!-- 这个库让我们可以访问应用的很多信息,包括:/env、/info、/metrics、/health等 -->
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-actuator</artifactId>
		</dependency>
		<!-- 用于热部署 -->
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-devtools</artifactId>
		</dependency>		
	</dependencies>
	<dependencyManagement>
		<dependencies>
		<!-- 版本依赖管理,故之后添加依赖无需指定version -->
			<dependency>
				<groupId>org.springframework.cloud</groupId>
				<artifactId>spring-cloud-dependencies</artifactId>
				<version>Camden.SR2</version>
				<type>pom</type>
				<scope>import</scope>
			</dependency>
		</dependencies>
	</dependencyManagement>

	<build>
		<plugins>
			<!-- 用以为integration-test提供支持。 -->
			<plugin>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
			</plugin>
		</plugins>
	</build>
</project>

本地启动服务效果如下:


 

将用户微服务注册到spring-cloud-eureka服务中

 

先启动spring-cloud-eureka注册服务,然后再启动用户服务,让用户微服务注册到eureka服务中,如下图所示




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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值