springcloud其实没有进行硬编码,它是一直架构的体现,将原本一个整体的服务架构拆分成了无数的小模块,断开了原本一个整体架构直接的耦合程度,使各个小模块之间相互独立.,服务之间一般通过HTTP 的 RESTfuL API 进行通信协作.并使用springboot整合了许多其他的主流的框架.
springcloud本身并没有进行代码的编写,它更像是一个容器将所有流行的框架进行了整合.
通过 Spring Boot 风格进行再封装屏蔽掉了复杂的配置和实现原理,最终给开发者留出了一套简单易懂、易部署和易维护的分 布式系统开发工具包。
它利用Spring Boot的开发便利性巧妙地简化了分布式系统基础设施的开发
如
Spring Cloud 与 Dubbo 都是实现微服务有效的工具。
• Dubbo 只是实现了服务治理,而Spring Cloud 子项目分别覆盖 了微服务架构下的众多部件。
• Dubbo 使用 RPC 通讯协议,Spring Cloud 使用 RESTful 完成 通信,Dubbo 效率略高于 Spring Cloud。
总结:doubb只是实现了服务治理,而springcloud则有一系列完整的微服务的功能.功能更加全面,而dubbo则是效率更好.
因为springcloud是使用微服务的架构,而服务之间会存在相互调用的情况,如果我们使用restful基于http协议之间调用,相互调用之间的url地址就会写死在编码中,如果我们这时候要更换被调用方的url,这时候就需要更改原来程序的地址,这时候消费者方和生产者的url的地址就会紧密的耦合在一起,这是我们不想看到的.这时候就产生了注册中心的概念.
注册中心是将生产者端的url地址放在注册中心里,当消费者需要调用服务的时候不是直接去找生产者,而是去注册中心中拿取生产者的信息,然后再去调用生产者的服务,这时候若是生产者的地址发生了变化,对我们的程序基本没有影响,因为我们的地址都是从注册中心中拿取的.这样降低了服务和服务之间的耦合程度.
eureka和我们学习的注册中心zookeeper我感觉最大的不一样的地方就是eureka的服务端需要我们自己去手动构建.而之前学习的zookeeper好像只要配置xml文件,双击启动zookeeper的服务即可.
3.2.1使用springboot整合eureka,编写eureka的服务端.
父工程的pom.xml文件
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.0.RELEASE</version>
<relativePath/>
</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>Greenwich.RELEASE</spring-cloud.version>
</properties>
<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>
eureka的服务端的pom.xml文件
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- eureka-server -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
</dependencies>
eureka的服务端的application.yml文件
server:
port: 8761
# eureka 配置
# eureka 一共有4部分 配置
# 1. dashboard:eureka的web控制台配置
# 2. server:eureka的服务端配置
# 3. client:eureka的客户端配置
# 4. instance:eureka的实例配置
eureka:
instance:
hostname: localhost # 主机名
client:
service-url:
defaultZone: http://${
eureka.instance.hostname}:${
server.port}/eureka # eureka服务端地址,将来客户端使用该地址和eureka进行通信
register-with-eureka: false # 是否将自己的路径 注册到eureka上。eureka server 不需要的,eureka provider client 需要
fetch-registry: false # 是否需要从eureka中抓取路径。eureka server 不需要的,eureka consumer client 需要
eureka的服务端的启动类
@EnableEurekaServer
@SpringBootApplication
public class EurekaServer {
public static void main(String[] args) {
SpringApplication.run(EurekaServer.class, args);
}
}
eureka的生产者端的pom.xml文件
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- eureka-client -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
</dependencies>
eureka的生产者端的application.yml文件
server:
port: 8001
eureka:
instance:
hostname: localhost # 主机名
client:
register-with-eureka: true # 是否将自己的路径 注册到eureka上。eureka server 不需要的,eureka provider client 需要
fetch-registry: true # 是否需要从eureka中抓取路径。eureka server 不需要的,eureka consumer client 需要
service-url:
defaultZone: http://localhost:8761/eureka #这是注册中心的url地址
#设置当前应用的名称,将来会在eureka中application显示,将来要使用该名称获取路径
#用途一:不设置名称springEureka网页中的application的名字unknown
#用途二:在eureka的客户端中使用该名称获取路径
spring:
application:
name: eureka-provider
eureka的生产者端的启动类
@EnableEurekaClient
@SpringBootApplication
public class ProviderApp {
public static void main(String[] args) {
SpringApplication.run(ProviderApp.class, args);
}
}
eureka的生产者端的controller
@RestController
@RequestMapping("/goods")
public class GoodsController {
@Autowired
private GoodsService goodsService;
@RequestMapping("/findOne/{id}")
public Goods getGoodsById(@PathVariable Integer id) {
return goodsService.findGoodsById(id);
}
}
eureka的生产者端的service
@Service
public class GoodsService {
@Autowired
private GoodsDao goodsDao;
public Goods findGoodsById(Integer id) {
return goodsDao.findGoodsById(id);
}
}
eureka的生产者端的dao
@Repository
public class GoodsDao {
public Goods findGoodsById(Integer id) {
return new Goods(1, "华为手机", 3999, 999);
}
}
eureka的生产者端的实体类
/**
* 商品实体类
*/
public class Goods {
private int id;
private String title;//商品标题
private double price;//商品价格
private int count;//商品库存
public Goods() {
}
public Goods(int id, String title, double price, int count) {
this.id = id;
this.title = title;
this.price = price;
this.count = count;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public double getPrice() {
return price;
}
public void setPrice(double price) {
this.price = price;
}
public int getCount() {
return count;
}
public void setCount(int count) {
this.count = count;
}
}
eureka的消费者端的pom.xml文件
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--eureka-client-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
</dependencies>
eureka的消费者端的controller
@RestController
@RequestMapping("/order")
public class OderController {
//RESTFUL风格远程调用其他服务
@Autowired
private RestTemplate restTemplate;
//使用springcloud自动注入发现客户端
@Autowired
private DiscoveryClient discoveryClient;
@GetMapping("/goods/{id}")
public Goods findGoodsById(@PathVariable("id") int id) {
//这里就要使用到我们在配置文件中使用
// #用途二:在eureka的客户端中使用该名称获取路径
List<ServiceInstance> instances = discoveryClient.getInstances("EUREKA-PROVIDER");
if (instances == null || instances.size() == 0) {
return null;
}
ServiceInstance serviceInstance = instances.get(0);
String host = serviceInstance.getHost();
int port = serviceInstance.getPort();
String url = "http://" + host + ":" + port + "/goods/findOne/" + id;
//远程调用Goods服务中的findOne接口
Goods goods = restTemplate.getForObject(url, Goods.class);
return goods;
}
}
创建模板类,注册到spring容器中,使用 RestTemplate 完成远程调用
@Configuration
public class RestTempleConfig {
@Bean
public RestTemplate restTemplate() {
return new RestTemplate();
}
}
eureka的消费者端的实体类
public class Goods {
private int id;
private String title;//商品标题
private double price;//商品价格
private int count;//商品库存
public Goods() {
}
public Goods(int id, String title, double price, int count) {
this.id = id;
this.title = title;
this.price = price;
this.count = count;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public double getPrice() {
return price;
}
public void setPrice(double price) {
this.price = price;
}
public int getCount() {
return count;
}
public void setCount(int count) {
this.count = count;
}
}
eureka的消费者端的启动类
//激活发现客户端
@EnableDiscoveryClient
@EnableEurekaClient
@SpringBootApplication
public class ConsumerApp {
public static void main(String[] args) {
SpringApplication.run(ConsumerApp.class, args);
}
}
3.2.2实现eureka注册中心的高可用
如何实现eureka注册中心的高可用,使用集群搭建,使eureka的server相互注册
再在消费端配置多个注册中心的url.
eureka的server1在eureka-client-service-url-defaultZone中配置server2的url
server:
port: 8761
# eureka 配置
# eureka 一共有4部分 配置
# 1. dashboard:eureka的web控制台配置
# 2. server:eureka的服务端配置
# 3. client:eureka的客户端配置
# 4. instance:eureka的实例配置
eureka:
instance:
hostname: eureka-server1 # 主机名
client:
service-url:
defaultZone: http://eureka-server2:8762/eureka # eureka服务端地址,将来客户端使用该地址和eureka进行通信
register-with-eureka: false # 是否将自己的路径 注册到eureka上。eureka server 不需要的,eureka provider client 需要
fetch-registry: false # 是否需要从eureka中抓取路径。eureka server 不需要的,eureka consumer client 需要
spring:
application:
name: eureka-ah
eureka的server2中,在eureka-client-service-url-defaultZone中配置server1的url
最后消费者端配置多个url即可.
父工程的pom.xjm文件
<!--spring boot 环境 -->
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.0.RELEASE</version>
<relativePath/>
</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 版本-->
<spring-cloud.version>Greenwich.RELEASE</spring-cloud.version>
</properties>
<!--引入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>
consul的生产者端的pom.xml文件
<dependencies>
<!--consul 客户端-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-consul-discovery</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.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
</dependencies>
consul的生产者端的application.yml文件
server:
port: 8008
spring:
cloud:
consul:
host: localhost # consul 服务端的 ip
port: 8500 # consul 服务端的端口 默认8500
discovery:
service-name: ${
spring.application.name} # 当前应用注册到consul的名称
prefer-ip-address: true # 注册ip
application:
name: consul-provider # 应用名称
消费者端的application.yml文件
server:
port: 9000
spring:
cloud:
consul:
host: localhost # consul 服务端的 ip
port: 8500 # consul 服务端的端口 默认8500
discovery:
service-name: ${
spring.application.name} # 当前应用注册到consul的名称
prefer-ip-address: true # 注册ip
application:
name: consul-consumer # 应用名称
pom.xml文件
<dependencies>
<!--nacos-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
<version>0.2.2.RELEASE</version>
</dependency>
<dependency>
<groupId>com.alibaba.nacos</groupId>
<artifactId>nacos-client</artifactId>
<version>1.1.0</version>
</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.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
</dependencies>
生产者端的applicaton.yml文件
server:
port: 8000
spring:
cloud:
nacos:
discovery:
server-addr: 127.0.0.1:8848 # 配置nacos 服务端地址
application:
name: nacos-provider # 服务名称
消费者端的application.yml文件
server:
port: 9000
spring:
cloud:
nacos:
discovery:
server-addr: 127.0.0.1:8848 # 配置nacos 服务端地址
application:
name: nacos-consumer # 服务名称
为实现生产者端的高可用,使用集群搭建,并在消费者端进行负载均衡策略.
这里的负载均衡策略使用的就是ribbon.
父工程的pom.xml文件
<!--spring boot 环境 -->
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.0.RELEASE</version>
<relativePath/>
</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 版本-->
<spring-cloud.version>Greenwich.RELEASE</spring-cloud.version>
</properties>
<!--引入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>
还是使用eureka作为服务的治理
eureka的服务端的pom.xml文件
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- eureka-server -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
</dependencies>
eureka的服务端的启动类
server:
port: 8761
# eureka 配置
# eureka 一共有4部分 配置
# 1. dashboard:eureka的web控制台配置
# 2. server:eureka的服务端配置
# 3. client:eureka的客户端配置
# 4. instance:eureka的实例配置
eureka:
instance:
hostname: localhost # 主机名
client:
service-url:
defaultZone: http://${
eureka.instance.hostname}:${
server.port}/eureka # eureka服务端地址,将来客户端使用该地址和eureka进行通信
register-with-eureka: false # 是否将自己的路径 注册到eureka上。eureka server 不需要的,eureka provider client 需要
fetch-registry: false # 是否需要从eureka中抓取路径。eureka server 不需要的,eureka consumer client 需要
eureka的客户端
ribbon_provider服务的提供方的pom.xml文件
<dependencies>
<!--spring boot web-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- eureka-client -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
</dependencies>
因为服务的生产者端这里只是单纯的提供服务未作其他改变(代码参照上面的服务生产者端的代码),只是在启动的时候我们一次启动多个端口的服务.
在application.yml文件中将端口不设置成固定端口
server:
port: ${port}
在启动的时候配置vm options即可
ribbon_consumer服务的消费者的pom.xml文件
<dependencies>
<!--spring boot web-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- eureka-client -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
</dependencies>
消费者端的application.yml文件
server:
port: 9000
eureka:
instance:
hostname: localhost # 主机名
client:
register-with-eureka: true # 是否将自己的路径 注册到eureka上。eureka server 不需要的,eureka provider client 需要
fetch-registry: true # 是否需要从eureka中抓取路径。eureka server 不需要的,eureka consumer client 需要
service-url:
defaultZone: http://localhost:8761/eureka
#设置当前应用的名称,将来会在eureka中application显示,将来要使用该名称获取路径
#用途一:不设置名称springEureka网页中的application的名字unknown
#用途二:在eureka的客户端中使用该名称获取路径
spring:
application:
name: eureka-consumer
#使用配置方式来实现负载均衡的策略(两种方式,也可以使用配置类来使用)
EUREKA-PROVIDER:
ribbon:
NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule
ribbon:
ReadTimeout: 1000
ConnectTimeout: 1000
MaxAutoRetries: 0
MaxAutoRetriesNextServer: 1
eureka:
enabled: true
配置负载均衡策略
全局策略设置:针对所有的服务调用都起作用
若是我们想使用这种全局配置类的形式而不让这种全局配置影响整个服务,可以让这个类不放在springboot的启动类同级包或者子包下,而是另外存放一个包中,当使用局部配置时候在启动类上添加相应的注解即可.
@Configuration
public class RibbonGlobalLoadBalancingConfiguration {
/**
* 随机规则
*/
@Bean
public IRule ribbonRule() {
return new RandomRule();
}
}
针对单个服务的配置
方式一:
在application.yml配置文件中
user-service: #具体的服务名
ribbon:
NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule
方式二:
在启动类上添加 该注解
@RibbonClient(name="eureka-provider",configuration = MyRule.class)
RestTempleConfig
@Configuration
public class RestTempleConfig {
@LoadBalanced
@Bean
public RestTemplate restTemplate() {
return new RestTemplate();
}
}
Controller
@RestController
@RequestMapping("/order")
public class OderController {
//RESTFUL风格远程调用其他服务
@Autowired
private RestTemplate restTemplate;
//使用springcloud自动注入发现客户端
@Autowired
private DiscoveryClient discoveryClient;
@GetMapping("/goods/{id}")
public Goods findGoodsById(@PathVariable("id") int id) {
/**
* 使用ribbon简化resttemplate的调用
* 1.在声明restTemplate的bean的时候,添加一个注解,@LoadBalanced
* 2.在使用restTemplate发起请求时,需要定义url时,后三天:port可以替换为服务的提供方的名称
*/
String url = "http://EUREKA-PROVIDER/goods/findOne/" + id;
Goods goods = restTemplate.getForObject(url, Goods.class);
return goods;
}
}
启动类
//激活发现客户端
@EnableDiscoveryClient
@EnableEurekaClient
@SpringBootApplication
@RibbonClient(name = "EUREKA-PROVIDER", configuration = MyRule.class)
@RibbonClients
public class ConsumerApp {
public static void main(String[] args) {
SpringApplication.run(ConsumerApp.class, args);
}
}
外部的配置类,ribbon的负载均衡规则
@Configuration
public class MyRule {
@Bean
public IRule rule() {
return new RandomRule();
}
}
之前我们编写的代码的远程调用都是基于restrful风格的http协议的远程调用,消费者端调用生产者端使用resttemplate和url的地址拼接实现远程的服务调用.
而使用fegin之后Feign通过注解和模板的方式来定义其工作方式,参数(包括url、method、request和response等)非常直观地融入到了模板中.优雅的调用http协议的api.
实现步骤:
1、添加依赖 spring-cloud-starter-openfeign
2、启动类添加注解 @EnableFeignClients
3、编写客户端接口
pom.xml文件中加入
<!--feign-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
消费者端的controller中
@RestController
@RequestMapping("/order")
public class OrderController {
@Autowired
private RestTemplate restTemplate;
@Autowired
private GoodsFeign goodsFeign;
@GetMapping("/goods/{id}")
public Goods findGoodsById(@PathVariable("id") int id) throws InterruptedException {
/*
String url = "http://FEIGN-PROVIDER/goods/findOne/" + id;
// 3. 调用方法
Goods goods = restTemplate.getForObject(url, Goods.class);*/
Goods goods = goodsFeign.findOne(id);
return goods;
}
}
feign类
@FeignClient(value = "FEIGN-PROVIDER")
public interface GoodsFeign {
@GetMapping("/goods/findOne/{id}")
public Goods findOne(@PathVariable("id") int id);
}
启动类
@EnableDiscoveryClient // 激活DiscoveryClient
@EnableEurekaClient
@SpringBootApplication
@EnableFeignClients//开启feign
public class ConsumerApp {
public static void main(String[] args) {
SpringApplication.run(ConsumerApp.class, args);
}
}