SpringCloud
首先这是一个专门做微服务的框架,那么说明是微服务呢?微服务其实是一种架构风格,将原本的系统拆分成一个个独立的小型服务,服务间通过HTTP的RESTful API进行通信。
了解了做什么之后,我们来看看他是什么。
其实SpringCloud是一系列框架的有序集合,也就是说其实他没有重复地制造轮子,就只是单纯的一个集合。但是上面提到了微服务,那不是就和Dubbo与ZooKeeper重合了吗?但是其实SpringCloud作为一个容器集合,他的功能更为强大。
其实我们要有一个概念,就是越封装,功能越强大,越齐全,但是同时其实性能也会更低。
比如SpringMVC就是Servlet的一个封装。
SpringCloud服务中心(注册中心)
Eureka Consul Nacos,这是主流的三个注册中心,其实他们功能都是类似的,就是注册和发现微服务。因为比如说你有一个服务A和服务B,服务A作为Provider,服务B作为Consumer,那么B是如何找到A的呢?那肯定就是IP地址了嘛,而这些IP地址都存在注册中心中,而任何一个微服务地址的变动都会告知注册中心,这样的话,所有Consumer其实下次也能及时找到相应的服务了,而不会因为地址更改而无法进行服务。我们先从Eureka入手开始做一个简单的Demo:
第一步创建:Provider和Consumer
基本结构如图所示
一个注册中心(Parent),生产者(Provider)和消费者(Consumer)都是其子目录下的。
具体的代码就直接看这里面的就行了。
2.使用RestTemplate来进行Restful的请求服务,因为我们上面的这两个模块很显然是两个Module,之间并不互通的嘛。那么如何互通呢?给RestTemplate个URL然后调就是了嘛。
Consumer:
@RestController
@RequestMapping("/order")
public class OrderController {
@Autowired//我们已经提前注册为Bean了
private RestTemplate restTemplate;
@GetMapping("/goods/{id}")
public Goods findGoodsById(@PathVariable("id") int id){
String url = "http://localhost:8000/goods/findOne/"+id;
Goods goods = restTemplate.getForObject(url, Goods.class);
return goods;
}
}
Provider:
//相当于Controller+ResponseBody(不找页面)
@RestController
//相当于Servlet的映射
@RequestMapping("/goods")
public class GoodsController {
@Autowired
private GoodsService goodsService;
//用Get的方式传参,这是一种RestFul风格的开发
@GetMapping("/findOne/{id}")
//把路径中的参数赋值给当前的方法形参
public Goods findOne(@PathVariable("id") int id){
return goodsService.findOne(id);
}
}
这么做了以后我们就可以发起调用了,但是这时候我们发现一个问题,这个IP地址是写死的呀!String url = "http://localhost:8000/goods/findOne/"+id;
,所以为了高可用和日后维护的需要,我们就该注册中心上场啦!
到这一步为止,我们使用的都是SpringBoot的东西!我们甚至都还没用SpringCloud!
3.搭建Eureka Server服务:
其实也很简单,创建Eureka Server Module。在里面创建他的启动类以及注明他是个Eureka Server。
引入Eureka Server的依赖:
<!-- eureka-server -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
@SpringBootApplication
// 启用EurekaServer
@EnableEurekaServer
public class EurekaApp {
public static void main(String[] args) {
SpringApplication.run(EurekaApp.class,args);
}
}
当然最主要的其实还是他的application.yml文件
server:
port: 8761
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 需要
server:
enable-self-preservation: false # 关闭自我保护机制
eviction-interval-timer-in-ms: 3000 # 检查服务的时间间隔
到这一步其实就已经搭建完成Server了。接下来我们就要开始搭建Client了。
4.将Provider和Consumer配置为Eureka Client。
一样的先引入Client的依赖:
<!-- eureka-client -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
配置文件,告诉他Server地址在哪里,顺便取个名字:
server:
port: 9000
eureka:
instance:
hostname: localhost # 主机名
client:
service-url:
defaultZone: http://localhost:8761/eureka # eureka服务端地址,将来客户端使用该地址和eureka进行通信
register-with-eureka: true # 是否将自己的路径 注册到eureka上。eureka server 不需要的,eureka provider client 需要
fetch-registry: true # 是否需要从eureka中抓取路径。eureka server 不需要的,eureka consumer client 需要
spring:
application:
name: eureka-consumer #配置个名字给他,免得是unknown
最后在启动类里注明一下这个是Client:
@EnableEurekaClient//注册为Eureka的Client
@SpringBootApplication
public class ConsumerApp {
public static void main(String[] args) {
SpringApplication.run(ConsumerApp.class);
}
}
到这里,所有的就都已经完成了。
5.加入注册中心后尝试远程动态调用服务
其实非常简单,因为之前我们只用SpringBoot,那个不是写死的了吗?这时候我们已经注册了服务中心了,我们直接根据在服务中心注册的名字来调用其中的IP和Port就好了。参数用Restful风格传递即可。
public Goods findGoodsById(@PathVariable("id") int id){
//获取到eureka-provider这个名字的微服务的实体类
List<ServiceInstance> instances = discoveryClient.getInstances("eureka-provider");
if(instances==null||instances.size()==0) return null;
//因为后期我们部署的时候其实是一个集群,所以这里是一个List
ServiceInstance serviceInstance = instances.get(0);
//这里就是根据其中的一个集群Server来获得他的IP和端口号,然后来获得服务
String host = serviceInstance.getHost();
int port = serviceInstance.getPort();
String url = "http://"+host+":"+port+"/goods/findOne/"+id;
Goods goods = restTemplate.getForObject(url, Goods.class);
System.out.println(goods+"Oh yes!");
return goods;
}