什么是微服务?
微服务架构风格是一种将一个单一应用程序开发为一组小型服务的方法,每个服务运行在自己的进程中,服务间通信采用轻量级通信机制。
微服务架构的特征:
- 一个系统由多个微服务搭建。
- 每个微服务可独立运行在自己的进程里。
- 一个微服务关注特定的功能。
- 微服务之间通过轻量级的通信机制进行通信。
- 微服务之间可使用不同的语言与数据库存储技术。
- 全自动的部署机制。
优点:
- 易于开发和维护:一个微服务关注特定功能,代码量较少。
- 单个微服务启动较快:代码少,所以启动快。
- 局部修改容易部署:修改微服务只需重。
- 技术栈不受限:可结合项目业务及团队的特点,合理地选择技术栈。
- 按需伸缩:可根据需求,实现细粒度的扩展。
面临挑战:
- 运维要求较高:更多的微服务意味着更多的运维投入。
- 分布式固有的复杂性:系统容错、网络延迟、分布式事务等。
- 接口调整成本高:微服务之间通过接口进行通信。
- 重复劳动:可能就某一个相同功能重复编写多次。
Spring Cloud简介
Spring Cloud是在SpringBoot基础上构建的,用于快速构建分布式系统的通用模式的工具集,具有开箱即用的生产特征,大大提高开发效率,为微服务提供了完整的解决方案。
- 分布式/版本化配置
- 服务注册和发现
- 路由
- service - to - service调用
- 负载均衡
- 断路器
- 分布式消息传递
使用Spring Cloud的构建微服务
在此之间,前来介绍一下Eureka,其作用与Dubbo类似
Eureka是Netflix开源的服务发现组件,本身是一个基于REST的服务。包含Server和Client两部分。Spring
Cloud将它集成在子项目Spring Cloud Netflix中,从而实现微服务的注册与发现。
——Spring Cloud与Docker微服务架构实战
Eureka Server:存储微服务启动时的注册信息,提供服务发现得能力。
Eureka Client:微服务的消费端
开始代码
1.创建Eureka Server
创建一个Spring Boot工程eureka-server
注意Spring Boot与Spring Cloud版本必须对应,否则会报错。
[查看版本信息]https://spring.io/projects/spring-cloud
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>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.5.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.dfyang</groupId>
<artifactId>eureka-server</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>eureka-server</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</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-netflix-eureka-server</artifactId>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Greenwich.RELEASE</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>
application.yml
server:
port: 8761
spring:
application:
name: eureka-server
eureka:
client:
# 是否将自己注册到Eureka Server,由于当前应用为Eureka Service,故为false
register-with-eureka: false
# 是否从Eureka Server获取注册信息,由于是单点,所以为false
fetch-registry: false
# 与Eureka Server交互的地址,默认是http://localhost:8761/eureka
service-url:
defaultZone: http://localhost:8761/eureka/
我看有的文章使用的是default-zone,我之前也是使用的default-zone,因为这样看起来与其他的更匹配一些,但在使用Spring Security时出现了问题,不知道是不是版本的问题,建议使用defaultZone。
为Eureka Server添加Spring Security认证
在启动类上加上@EnableEurekaServer注解,表明是一个Eureka Server
package com.dfyang;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;
@SpringBootApplication
@EnableEurekaServer
public class EurekaServerApplication {
public static void main(String[] args) {
SpringApplication.run(EurekaServerApplication.class, args);
}
}
启动项目,访问localhost:8761
2.创建服务提供者service-producer
创建一个Spring Boot工程service-producer
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>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.5.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.dfyang</groupId>
<artifactId>service-producer</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>service-producer</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<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-netflix-eureka-client</artifactId>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Greenwich.RELEASE</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>
application.yml
server:
port: 8080
spring:
application:
name: service-producer
eureka:
client:
service-url:
defaultZone: http://localhost:8761/eureka/
启动类上加上@EnableEurekaClient,表明是一个Eureka Client。
每个服务提供者也能作为服务消费者消费服务
package com.dfyang;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
@SpringBootApplication
@EnableEurekaClient
public class ServiceProducerApplication {
public static void main(String[] args) {
SpringApplication.run(ServiceProducerApplication.class, args);
}
}
编写接口
package com.dfyang.serviceProducer.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class ProducerController {
@RequestMapping("/service")
public String service() {
return "服务A";
}
}
启动服务,输入localhost:8761
3.创建服务消费者service-consumer(eureka client)
创建一个Spring Boot工程service-consumer
pom.xml
注意这里多了一个spring-cloud-starter-netflix-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>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.5.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.dfyang</groupId>
<artifactId>service-consumer</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>service-consumer</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<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-netflix-eureka-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Greenwich.RELEASE</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>
application.yml
server:
port: 8081
spring:
application:
name: service-consumer
eureka:
client:
service-url:
defaultZone: http://localhost:8761/eureka/
在启动类似加上@EnableDiscoveryClient(该注解与@EnableEurekaClient均表明是一个Eureka客户端,但@EnableEurekaClient只能在Eureka使用)
同时将RestTemplate作为Bean交给spring管理
@LoadBalanced表示使用轮询,这里不加会报java.net.UnknownHostException,不知道是不是版本的问题
package com.dfyang;
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 ServiceConsumerApplication {
public static void main(String[] args) {
SpringApplication.run(ServiceConsumerApplication.class, args);
}
@Bean
@LoadBalanced
public RestTemplate restTemplate() {
return new RestTemplate();
}
}
消费者获取服务
service-producer是服务提供者的 [spring.application.name]
service是服务提供者提供的服务
package com.dfyang.serviceConsumer.controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
@RestController
public class ConsumerController {
@Autowired
private RestTemplate restTemplate;
@GetMapping("/consume")
public String consume() {
return restTemplate.getForObject("http://service-producer/service", String.class);
}
}
访问localhost:8081/consume