一、单体系统与微服务体系
在以往传统的企业系统架构中,所有的业务接口都被集中在同个单体应用中。在业务需求不庞大的情况下,这样的系统架构在开发、测试、部署时都还比较方便,但是随着企业的发展,更多的业务需求也随之而来,单体应用为了满足这些需求就必须增加相应的业务模块,单体应用就会显得越来越臃肿;由于单体应用的所有业务都运行在同一个系统进程中,即使我们只是修改了一个很小的功能,也需要将整个项目全量更新上线,进而导致影响其他功能;单体应用的架构和业务逻辑对所有参与的开发者都是开放的,这就带来了一定的风险,某个开发者的一个误操作就有可能影响整个系统。
基于以上几点,分布式的微服务体系应运而生,它将系统内的各个功能模块拆分成不同的服务,这些服务都可以独立部署和扩展;由于各服务都运行在各自的进程中,每个服务的更新或是中断都不会影响其他服务;不同的业务可以根据不同的需求去自定义技术平台、服务器配置、数据库配置等,从而避免出现“杀鸡用牛刀”等情况;开发者只需根据业务划分,维护自己负责的服务,降低了开发风险。
二、SpringCloud简介
SpringCloud是一个基于SpringBoot的微服务架构开发工具。他提供了微服务开发所需的配置管理、服务发现、断路器、智能路由、微代理、控制总线、全局锁、决策竞选、分布式会话和集群状态管理等组件。最重要的是,跟spring boot框架一起使用的话,会让你开发微服务架构的云服务非常好的方便。SpringBoot旨在简化创建产品级的 Spring 应用和服务,简化了配置文件,使用嵌入式web服务器,含有诸多开箱即用微服务功能。
三、核心结构分析
整个微服务体系主要由三个核心模块组成:Eureka服务注册中心、服务提供者、服务消费者。
Eureka服务注册中心:Eureka提供的服务端,用于注册、发现、
他们的逻辑关系如下图所示,稍后会结合具体实现,进行分析。
四、具体实现
1、创建Eureka服务注册中心:
1.1 创建SpringBoot工程
以idea为例,创建一个SpringBoot工程,在选择依赖的时候,在Cloud Discovery下找到EureKa Server并勾选,这样选择之后,idea就会帮助我们补充好pom中需要的依赖,其他一路next即可;
1.2 配置application.properties:
server.port:服务监听的端口
eureka.instance.hostname:eureka实例的主机名称;
eureka.client.register-with-eureka:是否将本项目作为服务注册到服务中心,由于本项目就是服务中心,所以就设置为false;
eureka.client.fetch-registry:是否需要检索服务,这里也不需要;
eureka.client.service-url.defaultZone:eureka服务中心的访问地址;
spring.application.name:eureka服务中心的应用名称;
server.port=1112
eureka.instance.hostname=localhost
eureka.client.register-with-eureka=false
eureka.client.fetch-registry=true
eureka.client.service-url.defaultZone=http://${eureka.instance.hostname}:${server.port}/eureka/
spring.application.name=eureka_server
1.3 设置Application,开启EurekaServer:
package com.eureka_center;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;
@SpringBootApplication
@EnableEurekaServer
public class EurekaCenterApplication {
public static void main(String[] args) {
SpringApplication.run(EurekaCenterApplication.class, args);
}
}
设置完成后,运行项目,看到如下界面证明运行成功:
2、创建并注册服务提供者:
2.1 创建一个Spring Boot应用
可以直接通过IDE创建,也可以进入https://start.spring.io/进行创建;
2.2 配置pom.xml
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.service_provider</groupId>
<artifactId>service_provider</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<name>service_provider</name>
<description>service_provider for Spring Cloud</description>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.5.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.SR1</spring-cloud.version>
</properties>
<dependencies>
<!--全栈web开发模块,包含嵌入式Tomcat、SpringMVC-->
<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>
<!--Eureka服务端依赖-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka</artifactId>
<version>1.3.6.RELEASE</version>
</dependency>
</dependencies>
<!--Spring Cloud 依赖-->
<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>
</project>
这里有一点要注意,就是SpringCloud的版本要与springboot相对应,我这里使用的SpringBoot的版本是2.0.5.RELEASE对应的SpringCloud版本是Finchley.SR1。(可以直接套用之前配置eureka服务中心的SpringCloud)
2.3 配置application.properties:
server.port=9001
spring.application.name=hello-service
eureka.client.service-url.defaultZone=http://localhost:1112/eureka/
2.4 新建一个RestController
DiscoveryClinet用于帮助客户端与eureka server互相协作:
1、向server注册服务实例;
2、向server服务租约;
3、服务关闭期间,向server取消租约;
4、查询服务实例列表;
这里创建了一个RestController,查询服务实例列表并打印所有服务,并且返回HelloWorld;
package com.service_provider.controller;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
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.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
/**
*
* @author yuyan
* @create 2018-10-23 14:31
**/
@RestController
public class HelloController {
private Logger logger = LoggerFactory.getLogger(this.getClass());
@Autowired
DiscoveryClient client;
@RequestMapping("/hello")
public String index(){
for(String service:client.getServices()){
List<ServiceInstance> services = client.getInstances(service);
for(ServiceInstance serviceInstance:services){
logger.info("/hello, host:" + serviceInstance.getHost() + ", service_id:" + serviceInstance.getServiceId());
}
}
return "Hello World";
}
}
2.5 设置Application,通过EnableDiscoveryClient注解,让该应用注册成为Eureka客户端应用;
package com.service_provider;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
@EnableDiscoveryClient
@SpringBootApplication
public class ServiceProviderApplication {
public static void main(String[] args) {
SpringApplication.run(ServiceProviderApplication.class, args);
}
}
设置完成后,运行项目,运行成功后就可以在EurekaCenter的管理界面上,查看到服务信息:
3、创建服务消费者
3.1 创建一个Spring Boot应用
3.2 配置pom.xml:
同样,需要引入SpringCloud和Eureka服务端依赖,这里多引入了一个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.ribbon_consumer</groupId>
<artifactId>ribbon_consumer</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<name>ribbon_consumer</name>
<description>ribbon_consumer</description>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.5.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.SR1</spring-cloud.version>
</properties>
<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.3.6.RELEASE</version>
</dependency>
<!--Finchley的SpringCloud不能使用这个-->
<!--<dependency>-->
<!--<groupId>org.springframework.cloud</groupId>-->
<!--<artifactId>spring-cloud-starter-ribbon</artifactId>-->
<!--</dependency>-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
</dependency>
<!--<dependency>-->
<!--<groupId>org.springframework.cloud</groupId>-->
<!--<artifactId>spring-cloud-starter-netflix-eureka-client</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>
</project>
3.3 配置application.properties:
server.port=9000
spring.application.name=consumer
eureka.client.service-url.defaultZone=http://localhost:1112/eureka/
3.4 配置Application主类:
与服务提供者类似,通过EnableDiscoveryClient注解,让服务消费者注册成为Eureka客户端应用,并且在该主类中创建RestTemplate的SpringBoot实例,通过LoadBalanced开启客户端负载均衡;
package com.service_consumer;
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;
@EnableDiscoveryClient
@SpringBootApplication
public class ServiceConsumerApplication {
@Bean
@LoadBalanced//开启客户端负载均衡
RestTemplate restTemplate(){
return new RestTemplate();
}
public static void main(String[] args) {
SpringApplication.run(ServiceConsumerApplication.class, args);
}
}
3.5 创建一个RestController,用来调用服务提供者的服务:
通过restTemplate以get方式调用服务名称为HELLO-SERVICE的hello接口,并且返回接口的结果;这里使用的是服务名,而不是具体的ip或是域名作为访问地址,服务名可以通过Eureka控制台获取,或者通过DiscoveryClient获得服务列表;
package com.service_consumer.controller;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
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.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
import java.util.List;
@RestController
public class ConsumerController {
private Logger logger = LoggerFactory.getLogger(this.getClass());
@Autowired
RestTemplate restTemplate;
@RequestMapping(value = "/hello_consumer", method = RequestMethod.GET)
public String helloConsumer(){
return restTemplate.getForEntity("http://HELLO-SERVICE/hello/",String.class).getBody();
}
}
配置完成后,运行项目,运行成功后,打开Eureka服务控制台:
可以看到现在有两个应用,分别是一个消费者和一个提供者,现在访问localhost:9000/hello_consumer:
成功的返回了服务提供者的返回值:Hello World
至此,一个基于基于Eureka服务中心的微服务体系就搭建成功了!