1.由一系列小服务组成
2.可以独立的运行在自己的计算机进程(服务于服务之间互不影响)
3.每个服务可以独立部署
4.服务都是基于分布式管理
微服务就是一系列服务功能组成,能单独泡在自己的进程中,每个服务独立开发,独立运行,分布式管理
容易测试
容易部署
开发效率低
代码维护难
部署不灵活(必须的全部项目开发好才能部署)
稳定性不高
扩展性不高
SpringCloud是一个涵盖多个子项目的开发工具集,集合了众多的开源框架,利用;了SpringBoot的开发便利性,实现了很多功能,如服务注册,服务注册发现,负载均衡等,SpringCloud在整合过程中主要是针对于NetFlix(耐非)开元组件的封装。
NetFlix是美国的一个在线视频网站,微服务业的翘楚,他是公认的大规模生产级微服务的接触实践者,NetFlix的开源组件已经在他大规模分布式微服务环境中警告多年的生产实战验证,因此spring cloud中很多组件都是基于NetFlix组件的封装。
官网:http://projects.spring.io/spring-cloud/
这里主要是针对springboot集成springcloud的相关配置
Spring Cloud Ribbon 是一个基于Http和TCP的客服端负载均衡工具,它是基于Netflix Ribbon实现的。通过SpringCloud的自动配置使得项目可以自动的给RestTemplate添加拦截器,实现类似于上一节负载均衡的作用。
@Data
@AllArgsConstructor
@NoArgsConstructor
@Accessors(chain = true)
public class HostPort {
private String host;//主机名
private String port;//端口
}
package com.baizhi.loadbalance;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpRequest;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Random;
//静态代理,通过该类重新实现实际访问的端口与主机名
@Slf4j
public class UsertDefinedHttpRequest implements HttpRequest {
//被代理的请求,源请求
private HttpRequest request;
//模拟端口主机名map与逻辑名的映射
Map> resourceMap = null;
//带参构造,为request,resourceMap赋值
public UsertDefinedHttpRequest(HttpRequest request){
this.request = request;
resourceMap.put("USER_SERVICE", Arrays.asList(
new HostPort("192.168.192.14","8989"),
new HostPort("192.168.192.15","8989"),
new HostPort("192.168.192.19","8989")
));
}
@Override
//不变
public String getMethodValue() {
return request.getMethodValue();
}
@Override
//主要是修改端口与主机名,并进行负载均衡
public URI getURI() {
//原来的URI
URI originURI = request.getURI();
//返回新的URI
URI newURI = null;
//拿到原始请求的主机名
String uriHost = originURI.getHost();
//如果访问的逻辑名是USER_SERVICE时
if(resourceMap.containsKey(uriHost)){
//拿到服务注册列表
List hostPorts = resourceMap.get(uriHost);
//随机访问哪一个服务
HostPort newHostPort = hostPorts.get(new Random().nextInt(hostPorts.size()));
//构建新的URI
try {
newURI = new URI(originURI.getScheme(),originURI.getUserInfo(),newHostPort.getHost(),Integer.valueOf(newHostPort.getPort()),originURI.getPath(),originURI.getQuery(),originURI.getFragment());
} catch (URISyntaxException e) {
e.printStackTrace();
}
}else{
newURI = originURI;//否则就不变动URI
}
log.info("=====新的URI:" + newURI);
return newURI;
}
@Override
//不变
public HttpHeaders getHeaders() {
return request.getHeaders();
}
}
import org.springframework.http.HttpRequest;
import org.springframework.http.client.ClientHttpRequestExecution;
import org.springframework.http.client.ClientHttpRequestInterceptor;
import org.springframework.http.client.ClientHttpResponse;
import java.io.IOException;
//自定义拦截器,拦截
public class UserDefinedLoadbanlanceInterceptor implements ClientHttpRequestInterceptor {
@Override
public ClientHttpResponse intercept(HttpRequest httpRequest, byte[] bytes, ClientHttpRequestExecution clientHttpRequestExecution) throws IOException {
return clientHttpRequestExecution.execute(new UsertDefinedHttpRequest(httpRequest),bytes);
}
}
@SpringBootApplication
public class WebApplication {
public static void main(String[] args) {
SpringApplication.run(WebApplication.class,args);
}
@Bean
public RestTemplate restTemplate(){
RestTemplate restTemplate = new RestTemplate();
//添加拦截器
restTemplate.getInterceptors().add(new UserDefinedLoadbanlanceInterceptor())
return restTemplate;
}
}
import com.baizhi.UserWebApplication;
import com.baizhi.entities.User;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.web.client.RestTemplate;
@SpringBootTest(classes ={UserWebApplication.class} )
@RunWith(SpringRunner.class)
public class RestTemplateTests {
@Autowired
private RestTemplate restTemplate;
private String prefix="http://USER-SERVICE";
@Test
public void testQuertUserById(){
for(int i=0;i<100;i++){
String url=prefix+"/user/restUserManager/queryUserById?id={id}";
User user = restTemplate.getForObject(url, User.class, 83);
}
}
}
Spring Cloud Eureka 是 Spring Cloud Netflix 微服务套件中的一部分, 它基于 Netflix Eureka 做了二次封装, 主要负责完成微服务架构中的服务治理功能。 Spring Cloud 通过为Eureka 增加了 Spring Boot 风格的自动化配置,我们只需通过简单引入依赖和注解配置就能让 Spring Boot构建的微服务应用轻松地与 Eureka 服务治理体系进行整合。
org.springframework.boot
spring-boot-starter-parent
2.1.5.RELEASE
org.springframework.cloud
spring-cloud-dependencies
Greenwich.SR1
pom
import
org.springframework.cloud
spring-cloud-starter-netflix-eureka-server
# 服务基本配置
server.port=9999
# 配置Eureka服务注册中心
# 服务实例的运行时主机名
eureka.instance.hostname=localhost
#注册的服务必须每隔60秒发送一次心跳,否则就会从服务列表中剔除
eureka.server.expected-client-renewal-interval-seconds=60
#时隔1000ms检测一次有没有宕机的实例
eureka.server.eviction-interval-timer-in-ms=1000
# 注册中心服务相关配置 ,如果收到的心跳低于期望心跳85%,则注册中心会进入`自我保护机制` - 不删除注册中心认定失效的节点
eureka.server.renewal-percent-threshold=0.85
#隔多长时间同步一次,是否达到阈值
eureka.server.wait-time-in-ms-when-sync-empty=10000
# 关闭Eureka自我注册和抓取功能
eureka.client.register-with-eureka=false
eureka.client.fetch-registry=false
必须配置eureka.instance.hostname否则Eureka会尝试自我注册
@SpringBootApplication
@EnableEurekaServer //开启服务注解
public class EurekaServerApplication {
public static void main(String[] args) {
SpringApplication.run(EurekaServerApplication.class,args);
}
}
一分钟期望收1 * 0.85 = 0次心跳—阈值为0
目前没有实例注册,所以也是0
org.springframework.cloud
spring-cloud-dependencies
Greenwich.SR1
pom
import
org.springframework.boot
spring-boot-starter-actuator
org.springframework.cloud
spring-cloud-starter-netflix-eureka-client
#服务实例主机名
spring.application.name=USER-SERVICE
#服务实例id
eureka.instance.instance-id=001
#是否优先显示地址
eureka.instance.prefer-ip-address=true
#20秒没法送心跳,就认定出问题了
eureka.instance.lease-expiration-duration-in-seconds=20
#10秒发一个心跳
eureka.instance.lease-renewal-interval-in-seconds=10
#注册服务
eureka.client.register-with-eureka=true
eureka.client.healthcheck.enabled=true
#不关心eureka自身变化
eureka.client.fetch-registry=false
#往哪里注册
eureka.client.service-url.defaultZone=http://localhost:9999/eureka/
spring:
application:
# 服务实例的主机名
name: USER-SERVICE
eureka:
instance:
instance-id: 001
prefer-ip-address: true
lease-expiration-duration-in-seconds: 20
lease-renewal-interval-in-seconds: 10
# 向Eureka注册中心注册服务
client:
register-with-eureka: true
fetch-registry: false
service-url:
defaultZone: http://localhost:9999/eureka/
因为默认Eureka中集成了Ribbon插件,因此主需要导入spring-cloud-starter-netflix-eureka-client即可。
org.springframework.cloud
spring-cloud-dependencies
Greenwich.SR1
pom
import
org.springframework.cloud
spring-cloud-starter-netflix-eureka-client
eureka.client.fetch-registry=true
eureka.client.register-with-eureka=false
eureka.client.service-url.defaultZone=http://localhost:8761/eureka/
@SpringBootApplication
public class WebApplication {
public static void main(String[] args) {
SpringApplication.run(WebApplication.class,args);
}
@Bean
@LoadBalanced
public RestTemplate restTemplate(){
RestTemplate restTemplate = new RestTemplate();
//添加拦截器
//restTemplate.getInterceptors().add(new UserDefinedLoadbanlanceInterceptor());
return restTemplate;
}
}
import com.baizhi.WebApplication;
import com.baizhi.entities.User;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.web.client.RestTemplate;
@SpringBootTest(classes = WebApplication.class)
@RunWith(SpringRunner.class)
public class RestTemplateTests1 {
@Autowired
private RestTemplate restTemplate;
@Test
public void testQueryUserById(){
String url="http://USER-SERVICE//user/restUserManager/queryUserById?id={id}";
User user = restTemplate.getForObject(url, User.class,5);
System.out.println(user);
}
}
org.springframework.boot
spring-boot-starter-parent
2.1.5.RELEASE
org.springframework.cloud
spring-cloud-dependencies
Greenwich.SR1
pom
import
org.springframework.cloud
spring-cloud-starter-netflix-eureka-server
org.springframework.boot
spring-boot-maven-plugin
server.port=1111
# 指定当前注册中心的服务名称
spring.application.name=eureka-registry
## 启用注册中心主动失效,并且每次主动失效检测间隔为5s 默认值60s
eureka.server.eviction-interval-timer-in-ms= 5000
## 设置eureka注册中心的响应更新时间
eureka.server.responseCacheUpdateIntervalMs=3000
eureka.server.responseCacheAutoExpirationInSeconds=60
eureka.server.expected-client-renewal-interval-seconds=60
eureka.server.renewal-percent-threshold=0.85
eureka.server.wait-time-in-ms-when-sync-empty=10000
## 配置注册中心的主机名
eureka.instance.instance-id = eureka-1
eureka.instance.hostname = 192.168.192.14
## 服务刷新时间配置,每隔这个时间会主动心跳一次
eureka.instance.lease-renewal-interval-in-seconds= 5
## 服务提供者被认定为丢失心跳超时,失效多久后被删除
eureka.instance.lease-expiration-duration-in-seconds=15
## 配置定时获取|抓取注册中心的数据时间
eureka.client.registry-fetch-interval-seconds= 5
eureka.client.instance-info-replication-interval-seconds= 5
## 配置集群中其他eureka实例,用于本eureka实例的注册方。
eureka.client.region=beijing
eureka.client.availability-zones.beijing=zone-2,zone-3
eureka.client.service-url.zone-2=http://192.168.192.15:1111/eureka/
eureka.client.service-url.zone-3=http://192.168.192.19:1111/eureka/
server.port=1111
# 指定当前注册中心的服务名称
spring.application.name=eureka-registry
## 启用注册中心主动失效,并且每次主动失效检测间隔为5s 默认值60s
eureka.server.eviction-interval-timer-in-ms= 5000
## 设置eureka注册中心的响应更新时间
eureka.server.responseCacheUpdateIntervalMs=3000
eureka.server.responseCacheAutoExpirationInSeconds=60
eureka.server.expected-client-renewal-interval-seconds=60
eureka.server.renewal-percent-threshold=0.85
eureka.server.wait-time-in-ms-when-sync-empty=10000
## 配置注册中心的主机名
eureka.instance.instance-id = eureka-2
eureka.instance.hostname = 192.168.192.15
## 服务刷新时间配置,每隔这个时间会主动心跳一次
eureka.instance.lease-renewal-interval-in-seconds= 5
## 服务提供者被认定为丢失心跳超时,失效多久后被删除
eureka.instance.lease-expiration-duration-in-seconds=15
## 配置定时获取|抓取注册中心的数据时间
eureka.client.registry-fetch-interval-seconds= 5
eureka.client.instance-info-replication-interval-seconds= 5
## 配置集群中其他eureka实例,用于本eureka实例的注册方。
eureka.client.region=beijing
eureka.client.availability-zones.beijing=zone-1,zone-3
eureka.client.service-url.zone-1=http://192.168.192.14:1111/eureka/
eureka.client.service-url.zone-3=http://192.168.192.19:1111/eureka/
server.port=1111
# 指定当前注册中心的服务名称
spring.application.name=eureka-registry
## 启用注册中心主动失效,并且每次主动失效检测间隔为5s 默认值60s
eureka.server.eviction-interval-timer-in-ms= 5000
## 设置eureka注册中心的响应更新时间
eureka.server.responseCacheUpdateIntervalMs=3000
eureka.server.responseCacheAutoExpirationInSeconds=60
eureka.server.expected-client-renewal-interval-seconds=60
eureka.server.renewal-percent-threshold=0.85
eureka.server.wait-time-in-ms-when-sync-empty=10000
## 配置注册中心的主机名
eureka.instance.instance-id = eureka-3
eureka.instance.hostname = 192.168.192.19
## 服务刷新时间配置,每隔这个时间会主动心跳一次
eureka.instance.lease-renewal-interval-in-seconds= 5
## 服务提供者被认定为丢失心跳超时,失效多久后被删除
eureka.instance.lease-expiration-duration-in-seconds=15
## 配置定时获取|抓取注册中心的数据时间
eureka.client.registry-fetch-interval-seconds= 5
eureka.client.instance-info-replication-interval-seconds= 5
## 配置集群中其他eureka实例,用于本eureka实例的注册方。
eureka.client.region=beijing
eureka.client.availability-zones.beijing=zone-1,zone-2
eureka.client.service-url.zone-1=http://192.168.192.14:1111/eureka/
eureka.client.service-url.zone-2=http://192.168.192.15:1111/eureka/
@SpringBootApplication
@EnableEurekaServer//开启服务注解
public class EurekaServiceApplication {
public static void main(String[] args) {
SpringApplication.run(EurekaServiceApplication.class,args);
}
}
java -jar Eureka-Server-1.0-SNAPSHOT.jar --spring.profiles.active=eureka-1
java -jar Eureka-Server-1.0-SNAPSHOT.jar --spring.profiles.active=eureka-1
java -jar Eureka-Server-1.0-SNAPSHOT.jar --spring.profiles.active=eureka-1
启动前灵台会报错,正常的,第三台启动就OK了
只需要在application.properties中改动服务地址
eureka.client.service-url.defaultZone=http://192.168.192.14:1111/eureka/,http://192.168.192.15:1111/eureka/,http://192.168.192.19:1111/eureka/