单体–>分布式–>SOA–>微服务–>…
这里的Nginx就是一个负载均衡器,它可按某种算法(轮询、ip_hash等)清请求路由分发到不同的后端服务器上,同类型负载均衡器有 HAproxy、LVS等。
负载均衡算法如:轮询、随机、权重。
每个功能模块使用并发数量不同,有些模块使用特别多,有些模块使用的量少
就目前而言,对于微服务业界并没有一个统一的、标准的定义(While there is no precise definition of this architectural style ) 。但通常而言,`微服务架构是一种架构模式或者说是一种架构风格,它提倡将单一应用程序划分成一组小的服务,每个服务运行独立的自己的进程中,服务之间互相协调、互相配合,为用户提供最终价值。服务之间采用轻量级的通信机制互相沟通(通常是基于 HTTP 的 RESTful API )` 。每个服务都围绕着具体业务进行构建,并且能够被独立地部署到生产环境、类生产环境等。
另外,应尽量避免统一的、集中式的服务管理机制,对具体的一个服务而言,应根据业务上下文,选择合适的语言、工具对其进行构建,可以有一个非常轻量级的集中式管理来协调这些服务。可以使用不同的语言来编写服务,也可以使用不同的数据存储。
从“Martin Fowler”提出观点来看,微服务架构 和 SOA 架构很像。总体来说,微服务:就是把单体应用进行进行细粒度的拆分,分成多个小(微)的服务,每个服务独立运行,每个服务只需要专注一个业务即可,并且每个服务都可以有自己的数据库(分库),服务之间互协调配合完成整个系统的业务。
简单的来说,微服务:就是将一个单体项目根据需求,拆分为多个模块,每个模块就是一个项目,每个项目都可以单独的做
技术选型、部署、运维、维护。再根据每个模块的并发量做集群。
微服务机构图
微服务特点
微服务拆分做集群带来的问题
解决问题的办法
Netfli Eureka(注册中心)
Netflix Ribbon/Feign (远程调用,客户端负载均衡)
Netflix Hystrix(断路器/熔断器)
Netflix Zuul(服务网关)
Spring Cloud Config (分布式配置/配置中心)
Spring Cloud Bus(消息总线)
Spring Cloud sleuth(微服务链路追踪)
Release Train | SpringBoot Version |
---|---|
Hoxton | 2.2.x |
Greenwich | 2.1.x |
Finchley | 2.0.x |
Edgware | 1.5.x |
Dalston | 1.5.x |
spring-cloud-parent //父项目--Project
pom.xml //父项目的pom
service-eureka-server-11000 //注册中心EurekaServer
service-user-server-31000 //用户服务EurekaClient ,提供者--model
service-order-server-21000 //订单服务EurekaClient ,消费者--model
<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.0modelVersion>
<groupId>com.lfggroupId>
<artifactId>spring-cloud-parentartifactId>
<packaging>pompackaging>
<version>1.0-SNAPSHOTversion>
<modules>
<module>service-eureka-11000module>
<module>service-order-21000module>
<module>service-user-31000module>
modules>
<properties>
<project.build.sourceEncoding>UTF-8project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8project.reporting.outputEncoding>
<java.version>1.8java.version>
properties>
<parent>
<groupId>org.springframework.bootgroupId>
<version>2.2.5.RELEASEversion>
<artifactId>spring-boot-starter-parentartifactId>
parent>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-dependenciesartifactId>
<version>Hoxton.SR3version>
<type>pomtype>
<scope>importscope>
dependency>
dependencies>
dependencyManagement>
project>
<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">
<parent>
<artifactId>spring-cloud-parentartifactId>
<groupId>com.lfggroupId>
<version>1.0-SNAPSHOTversion>
parent>
<modelVersion>4.0.0modelVersion>
<artifactId>service-eureka-11000artifactId>
<dependencies>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
dependency>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-netflix-eureka-serverartifactId>
dependency>
dependencies>
project>
提示
spring-cloud-starter-netflix-eureka-server
spring-boot-starter-web
package cn.lfg2000;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;
/**
* 注册中心启动类
* @EnableEurekaServer : 开启EurekaServer服务端
*/
@SpringBootApplication
@EnableEurekaServer
public class EurekaServerApp {
public static void main(String[] args) {
SpringApplication.run(EurekaServerApp.class);
}
}
server:
port: 11000 #端口号
eureka:
instance:
hostname: localhost #主机名
client: #客户端配置
register-with-eureka: false #false:EurekaServer自己不要注册到EurekaServer自己 ,只有EurekaClient才注册
fetch-registry: false #EurekaServer不要拉取服务的通信地址列表 ,只有EurekaClient才拉取地址列表
service-url:
defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
server: #注册中心的注册地址
enable-self-preservation: false #关闭自我保护警告
eureka.server.enable-self-preservation=false
来关闭EurekaServer的保护机制,这样可以确保注册中心中不可用的实例被及时的剔除,但是不推荐
<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">
<parent>
<artifactId>spring-cloud-parentartifactId>
<groupId>com.lfggroupId>
<version>1.0-SNAPSHOTversion>
parent>
<modelVersion>4.0.0modelVersion>
<artifactId>service-user-31000artifactId>
<dependencies>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
dependency>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-netflix-eureka-clientartifactId>
dependency>
dependencies>
project>
package cn.lfg2000;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
/**
* 用户的启动类
* @EnableEurekaClient: 标记该应用是 EurekaClient(客户端)
*/
@SpringBootApplication
@EnableEurekaClient //或者@EnableDiscoveryClient
public class UserApp {
public static void main(String[] args) {
SpringApplication.run(UserApp.class);
}
}
#端口号
server:
port: 31001
#指定服务的名字
spring:
application:
name: service-user
#注册到EurekaServer
eureka:
client:
serviceUrl:
defaultZone: http://localhost:11000/eureka/
instance: #使用ip进行注册
instance-id: service-user:31001 #实例ID
prefer-ip-address: true #开启,使用指定ip进行注册
package cn.lfg2000;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
@SpringBootApplication
@EnableEurekaClient
public class OrderApp {
public static void main(String[] args) {
SpringApplication.run(OrderApp.class);
}
}
server:
port: 21000
spring:
application:
name: service-order
eureka:
client:
service-url:
defaultZone: http://localhost:11000/eureka
instance:
prefer-ip-address: true #启动ip注册 默认使用计算机的名字注册
instance-id: service-order:21000
<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">
<parent>
<artifactId>spring-cloud-parentartifactId>
<groupId>com.lfggroupId>
<version>1.0-SNAPSHOTversion>
parent>
<modelVersion>4.0.0modelVersion>
<artifactId>service-entityartifactId>
<dependencies>
<dependency>
<groupId>org.projectlombokgroupId>
<artifactId>lombokartifactId>
dependency>
dependencies>
project>
package cn.lfg2000.pojo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@NoArgsConstructor
@AllArgsConstructor
@Data
public class User {
private Long id;
private String username;
}
package cn.lfg2000.pojo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@NoArgsConstructor
@AllArgsConstructor
@Data
public class Order {
private Long id;
private String desc;
private Double price;
private User user;
}
<dependency>
<groupId>com.lfggroupId>
<artifactId>service-entityartifactId>
<version>1.0-SNAPSHOTversion>
dependency>
package cn.lfg2000.controller;
import cn.lfg2000.pojo.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
//用户服务:暴露接口给订单访问
@RestController
public class UserController {
// 将用户服务的端口号注入port中
@Value("${server.port}")
private Integer port;
//订单服务来调用这个方法 http://localhost:31001/user/1
@GetMapping("/user/{id}")
public User getUserById(@PathVariable("id") long id){
// 模拟一个user对象
return new User(id,"一个人哭"+port);
}
}
package cn.lfg2000.config;
import com.netflix.loadbalancer.RandomRule;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;
@Configuration
public class RestTemplateConfig {
//配置一个RestTemplate ,Spring封装的一个机遇Restful风格的http客户端 工具
@Bean
/*@LoadBalanced*/
public RestTemplate getRestTemplate(){
return new RestTemplate();
}
/*@Bean
public RandomRule getRandomRule(){
return new RandomRule();
}*/
}
package cn.lfg2000.controller;
import cn.lfg2000.pojo.Order;
import cn.lfg2000.pojo.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
@RestController
public class OrderController {
@Autowired
private RestTemplate restTemplate;
@GetMapping("/order/{id}")
public Order getOrder(@PathVariable("id") long id){
String url = "http://localhost:31001/user/"+id;
User user = restTemplate.getForObject(url, User.class);
return new Order(id,"一刀999",99.99,user);
}
}
客户端负载均衡算法
。超时,重试
等。按照负载均衡算法(如简单轮询,随机连接等)向多个服务发起调用
(正好可以解决上面的问题),我们也很容易使用Ribbon实现自定义的负载均衡算法
。#注册到EurekaServer
eureka:
client:
serviceUrl:
defaultZone: http://localhost:11000/eureka/
instance: #使用ip进行注册
instance-id: service-user:31001
prefer-ip-address: true #开启,使用指定ip进行注册
#端口号
server:
port: 31001
#指定服务的名字
spring:
application:
name: service-user
#注册到EurekaServer
eureka:
client:
serviceUrl:
defaultZone: http://localhost:11000/eureka/
instance: #使用ip进行注册
instance-id: service-user:31000 #实例ID
prefer-ip-address: true #开启,使用指定ip进行注册
#端口号
server:
port: 31000
#指定服务的名字
spring:
application:
name: service-user
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-netflix-ribbonartifactId>
dependency>
package cn.lfg2000.config;
import com.netflix.loadbalancer.RandomRule;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;
@Configuration
public class RestTemplateConfig {
//配置一个RestTemplate ,Spring封装的一个机遇Restful风格的http客户端 工具
@Bean
@LoadBalanced
public RestTemplate getRestTemplate(){
return new RestTemplate();
}
/*@Bean
public RandomRule getRandomRule(){
return new RandomRule();
}*/
}
package cn.lfg2000.controller;
import cn.lfg2000.pojo.Order;
import cn.lfg2000.pojo.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
@RestController
public class OrderController {
@Autowired
private RestTemplate restTemplate;
@GetMapping("/order/{id}")
public Order getOrder(@PathVariable("id") long id){
String url = "http://service-user/user/"+id;
User user = restTemplate.getForObject(url, User.class);
return new Order(id,"一刀999",99.99,user);
}
}
/**
* 订单的启动类
*/
@SpringBootApplication
@EnableEurekaClient
public class OrderServerApplication1030
{
//配置一个RestTemplate ,Spring封装的一个机遇Restful风格的http客户端 工具
//@LoadBalanced :让RestTemplate有负载均衡的功能
@Bean
@LoadBalanced
public RestTemplate restTemplate(){
return new RestTemplate();
}
//负载均衡算法
@Bean
public RandomRule randomRule(){
return new RandomRule();
}
//省略...
ribbon:
NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule
service-user:
ribbon:
NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule
使用Ribbon进行服务通信时为了防止网络波动造成服务调用超时,我们可以针对Ribbon配置超时时间以及重试机制
也可以针对具体的服务进行超时配置:如"<服务名>.ribbon…"
ribbon:
ReadTimeout: 3000 #读取超时时间
ConnectTimeout: 3000 #链接超时时间
MaxAutoRetries: 1 #重试机制:同一台实例最大重试次数
MaxAutoRetriesNextServer: 1 #重试负载均衡其他的实例最大重试次数
OkToRetryOnAllOperations: false #是否所有操作都重试,因为针对post请求如果没做幂等处理可能会造成数据多次添加/修改
ribbon:
eager-load:
enabled: true #开启饥饿加载
clients: user-server #针对于哪些服务需要饥饿加载
``
ribbon:
NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule
service-user:
ribbon:
NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule
使用Ribbon进行服务通信时为了防止网络波动造成服务调用超时,我们可以针对Ribbon配置超时时间以及重试机制
也可以针对具体的服务进行超时配置:如"<服务名>.ribbon…"
ribbon:
ReadTimeout: 3000 #读取超时时间
ConnectTimeout: 3000 #链接超时时间
MaxAutoRetries: 1 #重试机制:同一台实例最大重试次数
MaxAutoRetriesNextServer: 1 #重试负载均衡其他的实例最大重试次数
OkToRetryOnAllOperations: false #是否所有操作都重试,因为针对post请求如果没做幂等处理可能会造成数据多次添加/修改
ribbon:
eager-load:
enabled: true #开启饥饿加载
clients: user-server #针对于哪些服务需要饥饿加载