不同模块部署在不同服务器上
作用:分布式解决网站高并发带来问题
多台服务器部署相同应用构成一个集群
作用:通过负载均衡设备共同对外提供服务
RPC 的全称是 Remote Procedure Call 是一种进程间通信方式。
它允许程序调用另一个地址空间(通常是共享网络的另一台机器上)的过程或函数,而不用程序员显式编码这个远程调用的细节。即无论是调用本地接口/服务的还是远程的接口/服务,本质上编写的调用代码基本相同。
比如两台服务器A,B,一个应用部署在A服务器上,想要调用B服务器上应用提供的函数或者方法,由于不在一个内存空间,不能直接调用,这时候需要通过就可以应用RPC框架的实现来解决
(1)RESTful是一种架构设计风格,提供了设计原则和约束条件,而不是架构。而满足这些约束条件和原则的应用程序或设计就是 RESTful架构或服务。
(2)SOAP,简单对象访问协议是一种数据交换协议规范,
是一种轻量的、简单的、基于XML的协议的规范。SOAP协议和HTTP协议一样,都是底层的通信协议,只是请求包的格式不同而已,SOAP包是XML格式的。
SOAP的消息是基于xml并封装成了符合http协议,因此,它符合任何路由器、 防火墙或代理服务器的要求。
soap可以使用任何语言来完成,只要发送正确的soap请求即可,基于soap的服务可以在任何平台无需修改即可正常使用。
(3)RPC就是从一台机器(客户端)上通过参数传递的方式调用另一台机器(服务器)上的一个函数或方法(可以统称为服务)并得到返回的结果。
RPC 会隐藏底层的通讯细节(不需要直接处理Socket通讯或Http通讯)
RPC 是一个请求响应模型。客户端发起请求,服务器返回响应(类似于Http的工作方式)
RPC 在使用形式上像调用本地函数(或方法)一样去调用远程的函数(或方法)。
几种比较典型的RPC的实现和调用框架。
(1)RMI实现,利用java.rmi包实现,基于Java远程方法协议(Java Remote Method Protocol)
和java的原生序列化。
(2)Hessian,是一个轻量级的remoting onhttp工具,使用简单的方法提供了RMI的功能。 基于HTTP协议,采用二进制编解码。
(3)thrift是一种可伸缩的跨语言服务的软件框架。thrift允许你定义一个描述文件,描述数据类型和服务接口。依据该文件,编译器方便地生成RPC客户端和服务器通信代码。
(4)SpringCloud 为开发人员提供了快速构建分布式系统的一些工具,包括配置管理、服务发现、断路器、路由、微代理、事件总线、全局锁、决策竞选、分布式会话等等。
业务系统分解为多个组件,让每个组件都独立提供离散,自治,可复用的服务能力
通过服务的组合和编排来实现上层的业务流程
作用:简化维护,降低整体风险,伸缩灵活
架构设计概念,各服务间隔离(分布式也是隔离),自治(分布式依赖整体组合)其它特性(单一职责,边界,异步通信,独立部署)是分布式概念的跟严格执行
SOA到微服务架构的演进过程
作用:各服务可独立应用,组合服务也可系统应用(巨石应用[monolith]的简化实现策略-平台思想)
SpringCloud 为开发人员提供了快速构建分布式系统的一些工具,包括配置管理、服务发现、断路器、路由、微代理、事件总线、全局锁、决策竞选、分布式会话等等。它运行环境简单,可以在开发人员的电脑上跑。另外说明spring cloud是基于Springboot的,所以需要开发中对Springboot有一定的了解,如果不了解的话可以看蚂蚁课堂SpringBoot课程。
服务提供者:提供服务被人调用
消费者:调用被人服务
在这里,我们需要用的的组件上Spring Cloud Netflix的Eureka ,eureka是一个服务注册和发现模块。
这里先简单说明使用eureka进行业务层隔离,实现项目服务化也可以理解为微服务,先简单上手进行操作,eureka使用分为三块,1是服务注册中心,2是服务生产模块,3是服务消费模块
关系调用说明:
Eureka包含了Server端和Client端组件。
Server端是服务注册中心,用于提供服务的注册与发现。Eureka支持高可用的配置,当集群中有分片出现故障时,Eureka就会转入自动保护模式,它允许分片故障期间继续提供服务的发现和注册,当故障分片恢复正常时,集群中其他分片会把他们的状态再次同步回来。
Client端组件包含服务消费者与服务生产者。在应用程序运行时,Eureka服务生产者会向注册中心注册自身提供的服务并每30s发送心跳来更新它的服务租约,当一段时间生产者没有向服务中心续租将会被移出服务提供注册表。同时也可以从服务端查询当前注册的服务信息并把他们缓存到本地并周期性的刷新服务状态。
新建父项目,选择maven结构:
选择路径:
Pom文件添加依赖
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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0modelVersion>
<parent>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-parentartifactId>
<version>2.2.1.RELEASEversion>
<relativePath/>
parent>
<groupId>com.ls.springcloudgroupId>
<artifactId>springcloudartifactId>
<version>0.0.1-SNAPSHOTversion>
<packaging>pompackaging>
<name>springcloudname>
<description>project for Spring Bootdescription>
<modules>
<module>eurekamodule>
modules>
<properties>
<java.version>1.8java.version>
<spring-cloud.version>Hoxton.RELEASEspring-cloud.version>
properties>
<dependencies>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starterartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
dependency>
dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-dependenciesartifactId>
<version>${spring-cloud.version}version>
<type>pomtype>
<scope>importscope>
dependency>
dependencies>
dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-maven-pluginartifactId>
plugin>
plugins>
build>
project>
新建模块Eureka
pom添加依赖:
4.0.0
com.ls.springcloud
springcloud
0.0.1-SNAPSHOT
com.ls.springcloud
eureka
0.0.1-SNAPSHOT
eureka
eureka 注册中心
org.springframework.cloud
spring-cloud-starter-netflix-eureka-server
org.springframework.boot
spring-boot-maven-plugin
添加配置文件application.yml
spring:
application:
name: eureka
eureka:
instance:
#注册中心地址
hostname: localhost
###客户端调用地址
client:
serviceUrl:
defaultZone: http://${eureka.instance.hostname}:8081/eureka/
###是否将自己注册到Eureka服务中,因为该应用本身就是注册中心,不需要再注册自己(集群的时候为true)
register-with-eureka: false
###是否从Eureka中获取注册信息,因为自己为注册中心,不会在该应用中的检索服务信息
fetch-registry: true
创建启动类:
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;
@SpringBootApplication
@EnableEurekaServer
public class EurekaServerTestApplication {
public static void main(String[] args) {
SpringApplication.run(EurekaServerTestApplication.class, args);
}
}
运行项目访问结果:
http://localhost:8081/
此处没有服务注册,所以没有发现服务。
创建一个服务提供者 (eureka client),当client向server注册时,它会提供一些元数据,例如主机和端口,URL,主页等。Eureka server 从每个client实例接收心跳消息。 如果心跳超时,则通常将该实例从注册server中删除
Pom文件添加依赖:
springcloud
com.ls.springcloud
0.0.1-SNAPSHOT
4.0.0
client
org.springframework.cloud
spring-cloud-starter-netflix-eureka-client
org.springframework.boot
spring-boot-starter-web
org.springframework.boot
spring-boot-maven-plugin
添加application.yml配置文件
server:
port: 8082
enable-self-preservation: false
spring:
application:
name: eureka-server
eureka:
instance:
#注册中心地址
hostname: localhost
client:
serviceUrl:
defaultZone: http://${eureka.instance.hostname}:8081/eureka/
创建启动类:
@EnableEurekaClient
@SpringBootApplication
public class EurekaClientApplication {
public static void main(String[] args) {
SpringApplication.run(EurekaClientApplication.class, args);
}
}
创建controller:
@RestController
public class UserController {
@Value("${server.port}")
String port;
@RequestMapping("/hi")
public String home(@RequestParam String name) {
return "hi " + name + ",i am from port:" + port;
}
}
查看注册中心:
表示该服务已经注册,可以查看端口。
访问:http://localhost:8082/hi?name=li
在微服务架构中,业务都会被拆分成一个独立的服务,服务与服务的通讯是基于http restful的。Spring cloud有两种服务调用方式,一种是ribbon+restTemplate,另一种是feign。
ribbon是一个负载均衡客户端,可以很好的控制htt和tcp的一些行为。Feign默认集成了ribbon。
Pom文件添加依赖:
springcloud
com.ls.springcloud
0.0.1-SNAPSHOT
4.0.0
ribbon
org.springframework.cloud
spring-cloud-starter-netflix-eureka-client
org.springframework.cloud
spring-cloud-starter-netflix-ribbon
org.springframework.boot
spring-boot-starter-web
org.springframework.boot
spring-boot-maven-plugin
添加配置文件:
server:
port: 8084
enable-self-preservation: false
spring:
application:
name: ribbon
eureka:
instance:
#注册中心地址
hostname: localhost
client:
serviceUrl:
defaultZone: http://${eureka.instance.hostname}:8081/eureka/
添加启动类
package com.ls.ribbon;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.web.client.RestTemplate;
@EnableAutoConfiguration
@ComponentScan(basePackages={"com.ls.ribbon.controller","com.ls.ribbon.service"})
@EnableEurekaClient
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
@Bean
@LoadBalanced
RestTemplate restTemplate() {
return new RestTemplate();
}
}
创建controller:
package com.ls.ribbon.controller;
import com.ls.ribbon.service.HelloService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class HelloControler {
@Autowired
private HelloService helloService;
@RequestMapping(value = "/hi")
public String hi(@RequestParam String name) {
return helloService.hiService(name);
}
}
创建service:
package com.ls.ribbon.service;
//import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;
@Service
public class HelloService {
@Autowired
RestTemplate restTemplate;
public String hiService(String name) {
return restTemplate.getForObject("http://EUREKA-CLIENT/hi?name="+name,String.class);
}
}
演示效果:
注册中心:
http://localhost:8084/hi?name=li
这说明当我们通过调用restTemplate.getForObject(“http://EUREKA-CLIENT /hi?name=“+name,String.class)方法时,已经做了负载均衡,访问了不同的端口的服务实例。
Feign是一个声明式的伪Http客户端,它使得写Http客户端变得更简单。使用Feign,只需要创建一个接口并注解。它具有可插拔的注解特性,可使用Feign 注解和JAX-RS注解。Feign支持可插拔的编码器和解码器。Feign默认集成了Ribbon,并和Eureka结合,默认实现了负载均衡的效果。
简而言之:
启动eureka,端口为8081; 启动eureka-client 两次,端口分别为8082 、8083.
添加依赖
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">
<parent>
<artifactId>springcloudartifactId>
<groupId>com.ls.springcloudgroupId>
<version>0.0.1-SNAPSHOTversion>
parent>
<modelVersion>4.0.0modelVersion>
<artifactId>FeignartifactId>
<dependencies>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-netflix-eureka-clientartifactId>
dependency>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-openfeignartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
dependency>
dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-maven-pluginartifactId>
plugin>
plugins>
build>
project>
server:
port: 8085
enable-self-preservation: false
spring:
application:
name: feign
eureka:
instance:
#注册中心地址
hostname: localhost
client:
serviceUrl:
defaultZone: http://${eureka.instance.hostname}:8081/eureka/
package com.ls.controller;
import com.ls.service.FeignService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class IndexController {
@Autowired
private FeignService feignService;
@RequestMapping(value = "/hi",method = RequestMethod.GET)
public String hi(String name) {
return feignService.hi(name);
}
}
@Repository
@FeignClient(value = "EUREKA-CLIENT")
public interface FeignService {
@RequestMapping(value = "/hi", method = RequestMethod.GET)
public String hi(@RequestParam(value = "name") String name);
}
@SpringBootApplication
@EnableEurekaClient
@EnableFeignClients
public class SericeFeignApp {
public static void main(String[] args) {
SpringApplication.run(SericeFeignApp.class, args);
}
}
http://localhost:8085/hi?name=li 刷新交替显示8082 8083
在微服务架构中,根据业务来拆分成一个个的服务,服务与服务之间可以相互调用(RPC),在Spring Cloud可以用RestTemplate+Ribbon和Feign来调用。为了保证其高可用,单个服务通常会集群部署。由于网络原因或者自身的原因,服务并不能保证100%可用,如果单个服务出现问题,调用这个服务就会出现线程阻塞,此时若有大量的请求涌入,Servlet容器的线程资源会被消耗完毕,导致服务瘫痪。服务与服务之间的依赖性,故障会传播,会对整个微服务系统造成灾难性的严重后果,这就是服务故障的“雪崩”效应。
为了解决这个问题,业界提出了断路器模型。
Netflix开源了Hystrix组件,实现了断路器模式,SpringCloud对这一组件进行了整合。 在微服务架构中,一个请求需要调用多个服务是非常常见的,如下图:
较底层的服务如果出现故障,会导致连锁故障。当对特定的服务的调用的不可用达到一个阀值(Hystric 是5秒20次) 断路器将会被打开。
断路打开后,可用避免连锁故障,fallback方法可以直接返回一个固定值。
这篇文章基于上一篇文章的工程,首先启动上一篇文章的工程,启动eureka工程;启动eureka工程,它的端口为8082。
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-netflix-ribbonartifactId>
dependency>
package com.ls.ribbon.service;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;
@Service
public class HelloService {
@Autowired
RestTemplate restTemplate;
@HystrixCommand(fallbackMethod = "error")
public String hiService(String name) {
return restTemplate.getForObject("http://EUREKA-CLIENT/hi?name="+name,String.class);
}
public String error(String name){
return "参数为:"+name+",调用EUREKA-CLIENT 失败啦!";
}
}
@EnableEurekaClient
@EnableCircuitBreaker // 开启hystrix断路器
@SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
@Bean
@LoadBalanced
RestTemplate restTemplate() {
return new RestTemplate();
}
}
关闭8082端口,访问会回调error方法: http://localhost:8084/hi?name=li
Feign是自带断路器的,在D版本的Spring Cloud中,它没有默认打开。需要在配置文件中配置打开它,在配置文件加以下代码:
feign:
hystrix:
enabled: true
基于service-feign工程进行改造,只需要在FeignClient的SchedualServiceHi接口的注解中加上fallback的指定类就行了:
package com.ls.service;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.stereotype.Repository;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
@Repository
@FeignClient(value = "EUREKA-CLIENT",fallback=SchedualServiceHiHystric.class)
public interface FeignService {
@RequestMapping(value = "/hi", method = RequestMethod.GET)
public String hi(@RequestParam(value = "name") String name);
}
package com.ls.service;
import org.springframework.stereotype.Component;
@Component
public class SchedualServiceHiHystric implements FeignService {
public String hi(String name) {
return "feign ... name:" + name + " 系統錯誤,调用接口失败~!!";
}
}
在Spring Cloud微服务系统中,一种常见的负载均衡方式是,客户端的请求首先经过负载均衡(zuul、Ngnix),再到达服务网关(zuul集群),然后再到具体的服务。
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">
<parent>
<artifactId>springcloudartifactId>
<groupId>com.ls.springcloudgroupId>
<version>0.0.1-SNAPSHOTversion>
parent>
<modelVersion>4.0.0modelVersion>
<artifactId>zuulartifactId>
<dependencies>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-netflix-eureka-clientartifactId>
dependency>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-netflix-zuulartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
dependency>
dependencies>
project>
server:
port: 8086
enable-self-preservation: false
spring:
application:
name: zuul
eureka:
instance:
#注册中心地址
hostname: localhost
client:
serviceUrl:
defaultZone: http://${eureka.instance.hostname}:8081/eureka/
zuul:
routes:
api-a:
path: /api-a/**
service-id: ribbon
api-b:
path: /api-b/**
service-id: feign
host:
socket-timeout-millis: 60000
connect-timeout-millis: 60000
添加启动类
package com.ls.zuul;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.cloud.netflix.zuul.EnableZuulProxy;
/**
* @description:
* @author: LiShun
* @time: 2020/1/7 9:01
*/
@EnableZuulProxy // 开启zuul
@EnableEurekaClient
@SpringBootApplication
public class ZuulApplication {
public static void main(String[] args) {
SpringApplication.run(ZuulApplication.class,args);
}
}
首先指定服务注册中心的地址为http://localhost:8081/eureka/,服务的端口为8086,服务名为zuul;以/api-a/ 开头的请求都转发给ribbon服务;以/api-b/开头的请求都转发给feign服务;
依次运行这五个工程;打开浏览器访问:http://localhost:8086/api-a/hi?name=li ;浏览器显示:
2在项目下新建子目录spring-cloud-config-file,然后新建三个文件
3内容分别是 from=git-dev-1.0、from=git-test-1.0、from=git-1.0
4 新建一个分支feature,新分支里面新建三个同名的文件,不过内容分别是from=git-dev-2.0、from=git-test-2.0、from=git-2.0
<dependencies>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-config-serverartifactId>
dependency>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-netflix-eureka-clientartifactId>
dependency>
dependencies>
server:
port: 8087
enable-self-preservation: false
spring:
application:
name: config-server
cloud:
config:
server:
git:
uri: https://gitee.com/ls199802/springcloud #git仓库地址
searchPaths: spring-cloud-config-file
label: master #默认分支
username: #公开仓库不需要账号密码
password:
eureka:
instance:
#注册中心地址
hostname: localhost
client:
serviceUrl:
defaultZone: http://${eureka.instance.hostname}:8081/eureka/
@SpringBootApplication
@EnableConfigServer // 开启Spring Cloud Config 的服务端功能
@EnableDiscoveryClient
public class ConfigApplication {
// 分别测试master分支和feature分支
//http://localhost:8087/xx/dev/feature
//http://localhost:8087/xx/test/master
//http://localhost:8087/xx/dev/master
//http://localhost:8087/xx/test //默认master分支
public static void main(String[] args) {
SpringApplication.run(ConfigApplication.class,args);
}
}
访问配置信息的URL与配置文件的映射关系如下:
上面的url会映射{application}-{profile}.properties对应的配置文件,其中{label}对应Git上不同的分支,默认是master。
访问:http://localhost:8087/springcloud/dev/feature
可以看到远程配置文件中参数的值。
<dependencies>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-config-clientartifactId>
dependency>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-netflix-eureka-clientartifactId>
dependency>
dependencies>
配置文件必须是bootstrap.yml或bootstrap.properties否则还是访问的是本机的8888端口。
spring:
application:
name: springcloud #对应gitee创建的项目
cloud:
config:
profile: dev #读取配置文件后缀
label: master #默认分支
discovery:
service-id: config-server # 配置中心的服务名称
enabled: true # 这里必须设置为true 否则还是会访问8888端口
eureka:
client:
service-url:
defaultZone: http://localhost:8081/eureka
server:
port: 8088
@RestController
public class Test {
// 通过@Value将gitee远程配置文件的from值写入到from中
@Value("${from}")
private String from;
// 客户端启动时候获取分支上的配置参数${from}时候,配置中心会从git仓库拉取colo-dev.properties,colo.properties等文件到本地
@RequestMapping(value= "/hi")
private String hi() {
System.out.println(from);
return from;
}
}
@EnableEurekaClient
@SpringBootApplication
public class TestApplication {
public static void main(String[] args) {
SpringApplication.run(TestApplication.class, args);
}
}
获取到了远程配置文件参数的值,创建客户端成功。
欢迎交流学习。