接触微服务不久,一直想写套集群服务注册的应用场景,正好项目空闲期,就来搭建一个eureka服务治理框架
主要分为两个角色 eureka server和eureka client 从整个结构来看, 可以分为三部分, 服务注册与发现,生产者,消费者或是调用者,集成的时候,这些微服务可以写在一个项目,多个配置文件也可以,我这边了直观简洁,复制了项目修改不同的配置
大致图是这样,有不对的地方大家可以指出,
服务注册中心,简要集成一个高可用集群,且三个eureka server之间互相注册自己,三者之间会进行互现通信。另外有两个服务提供者,以实现服务的负载均衡达到高可用性,三台服务注册中心相互之间会同步服务信息,因此服务消费者在任何一台服务注册中心都能发现所有已注册的服务——事实上,通常会找较近zone的服务注册中心发现服务,因为是云服务,有区域概念在里面。服务消费者通过服务注册中心获得目前已经注册的服务列表,通过客户端负载均衡选择某个服务提供者,然后发起远程调用。事实上,包括注册服务、续约、终止服务或者发现服务都是通过restful的形式,注册发现与销毁是根据 服务心跳连接来更新续约来确定实例的,如果开户服务注册的保护机制,服务挂掉服务实例仍然存,服务具体的存储形式是双层map,外层key为服务名,内层key为实例名。
1 现在开始写项目相关代码
这是整个项目结构
2 创建服务注册中心项目
我们写一个启动类,加上@EnableEurekaServer注解即可。
@SpringBootApplication
@EnableEurekaServer
@EnableDiscoveryClient
public class EurekaApplication {
public static void main(String[] args) {
SpringApplication.run(EurekaApplication.class, args);
}
}
pom.xml文件的依赖,这里我采用Spring boot的版本用2.0.3.RELEASE 官方默认版本配置,因为项目是自己需要打包到容器去运行,所以加了docker 插件来打包生成容器,如果不需要的话, 可以把容器那些插件删掉,不影响项目
<?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.springCloudEuraka</groupId>
<artifactId>spring-cloud-eureka</artifactId>
<version>1.0.0</version>
<packaging>jar</packaging>
<name>spring-cloud-eureka</name>
<description>Demo project for Spring Boot</description>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.3.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>
<version.jackson>2.9.2</version.jackson>
<java.version>1.8</java.version>
<docker.url>http://192.168.11.233:2375</docker.url>
<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>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
<plugin>
<groupId>com.spotify</groupId>
<artifactId>docker-maven-plugin</artifactId>
<configuration>
<dockerHost>${docker.url}</dockerHost>
<imageName>${project.name}:${project.version}</imageName>
<dockerDirectory>${project.basedir}/src/main/docker</dockerDirectory>
<skipDockerBuild>false</skipDockerBuild>
<resources>
<resource>
<targetPath>/</targetPath>
<directory>${project.build.directory}</directory>
<include>${project.build.finalName}.jar</include>
</resource>
</resources>
<!-- 强制docker在每次新的构建上覆盖镜像tags -->
<forceTags>true</forceTags>
</configuration>
</plugin>
</plugins>
</build>
</project>然后是application.yml文件:注释比较多, 不想看的可以删掉,不影响啥, 我是为了方面以后查看
server:
port: 5011
spring:
application:
name: euraka-server
eureka:
client:
registerWithEureka: true
fetchRegistry: true
server:
enable-self-preservation: false
serviceUrl:
defaultZone: http://peer2:5012/eureka/,http://peer3:5013/eureka/
instance:
hostname: peer1
#peer1、peer2和peer3需要在hosts文件中设置为127.0.0.1,在生产环境中可以写真实的域名
metadataMap:
instanceId: ${spring.application.name}:${spring.application.instance_id:${random.value}}
#1 这里peer1、peer2和peer3需要在hosts文件中设置为127.0.0.1,在生产环境中可以写真实的域名
#2 注册中心集群模式,一定要自身注册到服务上,被其它节点发现registerWithEureka: true
#如果是单eureka server的环境,就将eureka.client.serviceUrl.defaultZone设置为本机的服务注册地址,
#同时需要设置eureka.client.registerWithEureka和eureka.client.fetchRegistry为false,以防止自己注册自己,集群的话设置为true。同时设置应用名为eureka-server
# Eureka Server会将当前的实例注册信息保护起来,同时提示这个警告
#3 spring.application.name或eureka.instance.appname必须一致;否则unavailable-replicas 界面显示是不可到达,或服务不可用
#4 enable-Self-Preservation 自我保护模式, Eureka Server在运行期间,会统计心跳失败的比例在15分钟之内是否
#低于85%,如果出现低于的情况(在单机调试的时候很容易满足,实际在生产环境上通常是由于网络不稳定导致),Eureka Server会将当前的实例注册信息保护起来,
#同时提示这个警告。保护模式主要用于一组客户端和Eureka Server之间存在网络分区场景下的保护。一旦进入保护模式,#Eureka Server将会尝试保护其服务注册表中的信息,不再删除服务注册表中的数据(也就是不会注销任何微服务)
特别提示下:
这里peer1、peer2和peer3需要在hosts文件中设置为127.0.0.1,在生产环境中可以写真实的域名
设置端口为5011,当前实例主机名为peer1 eureka.client.serviceUrl.defaultZone为另外两台eureka server的服务注册地址
另外两台eureka server的pom.xml文件一样,除了修改端口 application.yml文件中的eureka.client.serviceUrl.defaultZone分别设置为另外两个eureka server的服务地址 ,另外两台的端口是5012 主机名peer2 5013和peer3
3、建立服务提供者
pom.xml文件依赖 比服务注册多一个包,并且跟springBoot1.5版本差距还是很大,都 在netflix 文件里
<?xml versinotallow="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:schemaLocatinotallow="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.euraka</groupId>
<artifactId>springCloudEurakaProvider1</artifactId>
<version>1.0.0</version>
<packaging>jar</packaging>
<name>springCloudEurakaProvider1</name>
<description>Demo project for Spring Boot</description>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.3.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>
<version.jackson>2.9.2</version.jackson>
<java.version>1.8</java.version>
<docker.url>http://192.168.11.233:2375</docker.url>
<spring-cloud.version>Finchley.RELEASE</spring-cloud.version>
</properties>
<dependencies>
<!-- 服务注册和服务发现 两者的版本必须一致 提供注册服务主也必须依赖服务注册包 ,所以client提供服务必须有eureka-server的依赖 ,eureka-serve微服务不需要client依赖 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</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>
<plugin>
<groupId>com.spotify</groupId>
<artifactId>docker-maven-plugin</artifactId>
<configuration>
<dockerHost>${docker.url}</dockerHost>
<imageName>${project.name}:${project.version}</imageName>
<dockerDirectory>${project.basedir}/src/main/docker</dockerDirectory>
<skipDockerBuild>false</skipDockerBuild>
<resources>
<resource>
<targetPath>/</targetPath>
<directory>${project.build.directory}</directory>
<include>${project.build.finalName}.jar</include>
</resource>
</resources>
</configuration>
</plugin>
</plugins>
</build>
</project>配置文件也与服务注册中心有所不同。
server:
port: 5022
eureka:
client:
serviceUrl:
defaultZone: http://peer1:5011/eureka/
healthcheck:
enabled: true
lease:
duration: 5
instance:
preferIpAddress: true
spring:
application:
name: springCloudEurakaProvider
#两个生产者应用史是一样的,这样微服务之间调用的时候只以做负载均衡,无需指定某个应用名
#logging:
# level:
# org.exampledriven.eureka.customer.shared.CustomerServiceFeignClient: FULL#lease 租期时间,不定时发送心跳包消息,如果长时间未能更新租期时间,服务就为抛弃该服务实例,注册中心会当成无用服务
springCloudEurakaProvider
然后我们写一个启动类,
ServerProvider1Application .class,加上EnableEurekaClient注解。当然我也加了其它的几种,就是为了用
Feign负载均衡
@SpringBootApplication
@Configuration
@EnableEurekaClient
@EnableDiscoveryClient
@EnableFeignClients
public class ServerProvider1Application {
public static void main(String[] args) {
SpringApplication.run(ServerProvider1Application.class, args);
}
@LoadBalanced
@Bean
public RestTemplate restTemplate() {
return new RestTemplate();
}
}
就写个最简单的hello world式的restful接口
@RestController
public class HelloController {
@RequestMapping("/hello")
public String hello(){
System.out.println("***************springCloudEurakaProvider1************hello");
return "hello world";
}
@RequestMapping("/world")
public String world(){
System.out.println("***************springCloudEurakaProvider1************world");
return "lmc world is beauful";
}
//使用Spring Netflix Feign Client实现的REST消费Web应用,远程微服务的接口,本质的接口都 一样,微服务调用只跟URL有关,跟方法名无关
@ResponseBody
@RequestMapping("/greeting")
public String greetingTest(@RequestBody String str) {
System.out.println("************springCloudEurakaProvider1***********greetingTest********"+str);
return "view_lmc_123456"+str;
}
}
现在springCloudEurakaProvider1基本完成 了, springCloudEurakaProvider2跟这个一样,只需要改下配置文件 端口设置成5023 defaultZone配置可以在三台中任意选 一个
server:
port: 5023
eureka:
client:
serviceUrl:
defaultZone: http://peer2:5012/eureka/
healthcheck:
enabled: true
lease:
duration: 5
instance:
preferIpAddress: true
spring:
application:
name: springCloudEurakaProvider
#两个生产者应用史是一样的,这样微服务之间调用的时候只以做负载均衡,无需指定某个应用名
#logging:
# level:
# org.exampledriven.eureka.customer.shared.CustomerServiceFeignClient: FULL
#lease 租期时间,不定时发送心跳包消息,如果长时间未能更新租期时间,服务就为抛弃该服务实例,注册中心会当成无用服务4、建立服务消费者
与之前的消息生产者一样,需要说明的是,1.5版本要加rabbon 的依赖,2.0.3版本不需要了,自动依赖一个人种模式的负载均衡Resttempte+Rabbon,和 Feign。默认是轮询的方式。
需要修改的也只有 配置文件
server:
port: 5080
eureka:
client:
serviceUrl:
defaultZone: http://peer1:5011/eureka/
healthcheck:
enabled: true
lease:
duration: 5
instance:
preferIpAddress: true
spring:
application:
name: springCloudEurakaConsumer
#logging:
# level:
# org.exampledriven.eureka.customer.shared.CustomerServiceFeignClient: FULL#lease 租期时间,不定时发送心跳包消息,如果长时间未能更新租期时间,服务就为抛弃该服务实例,注册中心会当成无用服务
启动类,这里一样加上EnableEurekaClient的注解。同时创建RestTemplate的bean,再加上EnableFeignClients后面两种负载均衡一起做,加上LoadBalanced注解用于负载均衡
@SpringBootApplication
@Configuration
@EnableEurekaClient
@EnableDiscoveryClient
@EnableFeignClients
public class DemoServerApplication {
public static void main(String[] args) {
SpringApplication.run(DemoServerApplication.class, args);
}
@LoadBalanced
@Bean
public RestTemplate restTemplate() {
return new RestTemplate();
}}
接下来是具体的消费类的代码 因为用到Feign方式调用微服务, 所以写了个接口
@RestController
public class CustomerController {
//微服务之间调用的方式有两种,RestTemplate+ribbon 实现负载均衡,第二种是Feign Client
//spring-cloud-starter-ribbon的依赖用于客户端负载均衡,默认是轮询的方式。
@Autowired
RestTemplate restTemplate;
@GetMapping("/helloCustomer")
public String helloCustomer(){
return restTemplate.getForEntity("http://springCloudEurakaProvider/hello", String.class).getBody();
}
@GetMapping("/customerWorld")
public String worldCustomer(){
return restTemplate.getForEntity("http://springCloudEurakaProvider/world", String.class).getBody();
}
//使用Spring Netflix Feign Client实现的REST消费Web应用,本地服务调用远程微服务的接口
@Autowired
private GreetingClient greetingClient;
@RequestMapping("/get-greeting")
public String greeting(Model model) {
Map<String, String> parpm= new HashMap<String, String>();
parpm.put("name", "lmc");
parpm.put("test", "demo");
String message = greetingClient.greeting(parpm.toString()); //以字符串方式传参数
return message+":传参数";
}}
Feign Client 调用微服务的接口 GreetingClient
//Feign Client 调用微服务程序,接口方式实现,可以很好的将微服务分开,松耦合
@FeignClient("springCloudEurakaProvider")
public interface GreetingClient {
/**
* 这里有两个坑需要注意:
*
* 1、这里需要设置请求的方式为 RequestMapping 注解,用 GetMapping 注解是运行不成功的,即 GetMapping 不支持。
*
* 2、注解 PathVariable 里面需要填充变量的名字,不然也是运行不成功的。
* 3、这一方法主要就是为了绑定Controller里,访问url的方法,控制层通过方法去请求找到相应的URL方法
* 通过接口方式绑定远程微服务
*/
/* @RequestMapping(value="/greeting",method = RequestMethod.POST)
String greeting(JSONObject parpam);*/
@PostMapping(value="/greeting")//传参数的方式调用
String greeting(String str);
}
5、启动并验证
到这里就基本结束简单的配置信息,开始测试能不能跑通了,实践过程中由于版本和环境的问题, 不知道坑了多少次,都 是一步步探着坑走过来的,
开始先启动spring-cloud-eureka、spring-cloud-eureka2和spring-cloud-eureka3。启动过程中控制台中可能会有warn或者error信息,如下面的信息,这是因为peer2和peer3还未启动,所以出现连接失败的情况,当三个eureka server全部启动后,系统就是完全正常运行的。
再查看服务注册中心的相关信息,访问peer1:1111或者peer2:1112或者peer3:1113可以查看。
可以看到服务已经注册上了
三个服务注册中心,两个服务生产者, 一个服务消费都 ,都 在这上面运行,这样说明一切正常了
最后验证服务是否调用成功,访问http://localhost:5080/customerWorld 成功得到如下结果:
再然后可以看看负载均衡,在地址栏多次刷新,查看打印输出的信息会得到不同实例输出的结果,
说明 负载均衡正常