有过dubbo/dubbox使用经验的朋友,看到下面这张图,一定很熟悉,就是SOA架构的最基本套路。

与dubbo对比,上图的3大要素中,spring cloud是借助以下组件来实现的:

1、注册中心:

spring cloud默认使用eureka server来做注册中心,而dubbo默认使用的是zookeeper。eureka的注册信息是保存在一个双层的Map对象中的,换句话说在内存中,不象zookeeper是长久保存在节点中。

2、服务提供方:

spring-web(Spring MVC)提供了完善的http rest服务框架,用这一套就能提供rest服务。(目前spring cloud官方提供的示例基本上都是http rest服务,理论上讲,应该也可以扩展成rpc服务,而dubbo是以rpc为主的,这点有些区别)

3、服务消费方:

依赖于spring-web,负载均衡采用ribbon组件来完成,大致原理是从注册中心发现可用服务的信息,缓存在本地,然后按一定的负载均衡算法进行调用。(跟dubbo类似,只不过dubbo是自己实现的负载均衡)

下面是这三方的最基本示例:

一、项目结构

注:spring-cloud是完全基于Spring Boot来构建项目的,所以对spring boot不熟悉的,建议先看本博客的spring boot系列

register-center 即 eureka 注册中心

service-api 为服务契约

service-consumer 为服务消费方

service-provider 为服务提供方

二、register-center

2.1 依赖项

  1. buildscript {
  2. repositories {
  3. maven {
  4. url "http://maven.aliyun.com/nexus/content/groups/public/"
  5. }
  6. }
  7. dependencies {
  8. classpath("org.springframework.boot:spring-boot-gradle-plugin:1.5.4.RELEASE")
  9. }
  10. }
  11.  
  12. apply plugin: 'spring-boot'
  13.  
  14. dependencyManagement {
  15. imports {
  16. mavenBom "org.springframework.cloud:spring-cloud-dependencies:Dalston.RELEASE"
  17. }
  18. }
  19.  
  20. dependencies {
  21. compile 'org.springframework.cloud:spring-cloud-starter-eureka-server'
  22. compile 'org.springframework.boot:spring-boot-starter-actuator'
  23. testCompile 'org.springframework.boot:spring-boot-starter-test'
  24. }

2.2 main入口程序

  1. package com.cnblogs.yjmyzz.spring.cloud.study;
  2.  
  3. import org.springframework.boot.SpringApplication;
  4. import org.springframework.boot.autoconfigure.SpringBootApplication;
  5. import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;
  6.  
  7. /**
  8. * Created by 菩提树下的杨过 on 2017/6/17.
  9. */
  10. @SpringBootApplication
  11. @EnableEurekaServer
  12. public class RegisterServer {
  13.  
  14. public static void main(String[] args) {
  15. SpringApplication.run(RegisterServer.class, args);
  16. }
  17. }  

主要是靠最上面的@EnableEurekaServer这个注解,其它完全没有花头。

2.3 配置

  1. server:
  2. port: 8000
  3.  
  4. eureka:
  5. client:
  6. register-with-eureka: false
  7. fetch-registry: false
  8. service-url:
  9. defaultZone: http://localhost:8000/eureka  

解释一下:

注册中心本身也是一个服务,也可以当成普通服务向其它注册中心来注册,由于本示例中,只有一个eureka server自己就充当注册中心,也不需要跟其它注册中心同步注册信息,所以都设置成false。最后一行的defaultZone,初次接触可以先不管,先理解成注册中心对外暴露的地址即可。

2.4 启动

启动后,浏览http://localhost:8000/,可以看到类似下图:

现在没有任何服务注册,所以在Application里,显示No instances available.

三、service-api

为了方便后面讲解,先定义一个服务接口,以及对应的DTO

  1. package com.cnblogs.yjmyzz.spring.cloud.study.api;
  2.  
  3. import com.cnblogs.yjmyzz.spring.cloud.study.dto.UserDTO;
  4.  
  5. /**
  6. * Created by 菩提树下的杨过 on 2017/6/17.
  7. */
  8. public interface UserService {
  9.  
  10. UserDTO findUser(Integer userId);
  11. }

以及

  1. package com.cnblogs.yjmyzz.spring.cloud.study.dto;
  2.  
  3. import lombok.Data;
  4.  
  5. /**
  6. * Created by 菩提树下的杨过 on 2017/6/17.
  7. */
  8. @Data
  9. public class UserDTO {
  10.  
  11. private Integer userId;
  12.  
  13. private String userName;
  14. }

四、service-provider

4.1 依赖项

  1. buildscript {
  2. repositories {
  3. maven {
  4. url "http://maven.aliyun.com/nexus/content/groups/public/"
  5. }
  6. }
  7. dependencies {
  8. classpath("org.springframework.boot:spring-boot-gradle-plugin:1.5.4.RELEASE")
  9. }
  10. }
  11.  
  12. apply plugin: 'spring-boot'
  13.  
  14. dependencyManagement {
  15. imports {
  16. mavenBom "org.springframework.cloud:spring-cloud-dependencies:Dalston.RELEASE"
  17. }
  18. }
  19.  
  20. dependencies {
  21. compile(project(":service-api"))
  22. compile 'org.springframework.cloud:spring-cloud-starter-eureka'
  23. compile 'org.springframework.boot:spring-boot-starter-actuator'
  24. compile 'org.springframework.boot:spring-boot-starter-web'
  25. testCompile 'org.springframework.boot:spring-boot-starter-test'
  26. }

4.2 接口实现

  1. package com.cnblogs.yjmyzz.spring.cloud.study.service.impl;
  2.  
  3. import com.cnblogs.yjmyzz.spring.cloud.study.api.UserService;
  4. import com.cnblogs.yjmyzz.spring.cloud.study.dto.UserDTO;
  5. import org.springframework.stereotype.Service;
  6.  
  7. @Service("userService")
  8. public class UserServiceImpl implements UserService {
  9.  
  10. @Override
  11. public UserDTO findUser(Integer userId) {
  12. UserDTO user = new UserDTO();
  13. user.setUserId(userId);
  14. user.setUserName("菩提树下的杨过");
  15. return user;
  16. }
  17. }

这里只是随便示意一下,直接返回一个固定的UserDTO实例。

4.3 controller

  1. package com.cnblogs.yjmyzz.spring.cloud.study.controller;
  2.  
  3. import com.cnblogs.yjmyzz.spring.cloud.study.api.UserService;
  4. import com.cnblogs.yjmyzz.spring.cloud.study.dto.UserDTO;
  5. import org.springframework.beans.factory.annotation.Autowired;
  6. import org.springframework.web.bind.annotation.GetMapping;
  7. import org.springframework.web.bind.annotation.PathVariable;
  8. import org.springframework.web.bind.annotation.RestController;
  9.  
  10. @RestController
  11. public class UserController {
  12.  
  13. @Autowired
  14. private UserService userService;
  15.  
  16. @GetMapping("/user/{id}")
  17. public UserDTO findUser(@PathVariable Integer id) {
  18. return userService.findUser(id);
  19. }
  20. }

这里用了一个新的注解GetMapping,相当于之前SpringMVC中@RequestMapping(method = RequestMethod.GET),更简洁而已。

到目前为止,都跟常规的SpringMVC无异。

4.4 main入口

  1. package com.cnblogs.yjmyzz.spring.cloud.study;
  2.  
  3. import org.springframework.boot.SpringApplication;
  4. import org.springframework.boot.autoconfigure.SpringBootApplication;
  5. import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
  6.  
  7. /**
  8. * Created by yangjunming on 2017/6/17.
  9. */
  10. @EnableDiscoveryClient
  11. @SpringBootApplication
  12. public class ServiceProvider {
  13.  
  14. public static void main(String[] args) {
  15. SpringApplication.run(ServiceProvider.class, args);
  16. }
  17. }  

依旧还是@EnableDiscoveryClient挑大梁,表明这是一个eureka的客户端程序(即:能向eureka server注册)  

4.5 配置

  1. server:
  2. port: 8001
  3.  
  4. spring:
  5. application:
  6. name: "service-provider-demo"
  7. eureka:
  8. instance:
  9. prefer-ip-address: true
  10. client:
  11. service-url:
  12. defaultZone: http://localhost:8000/eureka/  

应该不难理解,最后那几行,表示用自己IP地址向 http://localhost:8000/eureka/注册

4.6 启动

启动成功后,再看eureka 刚才的页面,会发现已经注册进来了。

注:大家可以把service-provider多启动几个实例(端口错开,不要冲突即可),然后再观察下这个界面,可以看到注册了多个provider实例

五、service-consumer

5.1 依赖项

  1. buildscript {
  2. repositories {
  3. maven {
  4. url "http://maven.aliyun.com/nexus/content/groups/public/"
  5. }
  6. }
  7. dependencies {
  8. classpath("org.springframework.boot:spring-boot-gradle-plugin:1.5.4.RELEASE")
  9. }
  10. }
  11.  
  12. apply plugin: 'spring-boot'
  13.  
  14. dependencyManagement {
  15. imports {
  16. mavenBom "org.springframework.cloud:spring-cloud-dependencies:Dalston.RELEASE"
  17. }
  18. }
  19.  
  20. dependencies {
  21. compile(project(":service-api"))
  22. compile 'org.springframework.cloud:spring-cloud-starter-eureka'
  23. compile 'org.springframework.boot:spring-boot-starter-actuator'
  24. compile 'org.springframework.cloud:spring-cloud-starter-ribbon'
  25. compile 'org.springframework.boot:spring-boot-starter-web'
  26. testCompile 'org.springframework.boot:spring-boot-starter-test'
  27. }

5.2 建一个调用的Controller

  1. package com.cnblogs.yjmyzz.spring.cloud.study.service.controller;
  2.  
  3. import com.cnblogs.yjmyzz.spring.cloud.study.dto.UserDTO;
  4. import org.springframework.beans.factory.annotation.Autowired;
  5. import org.springframework.cloud.client.ServiceInstance;
  6. import org.springframework.cloud.client.discovery.DiscoveryClient;
  7. import org.springframework.cloud.client.loadbalancer.LoadBalancerClient;
  8. import org.springframework.web.bind.annotation.GetMapping;
  9. import org.springframework.web.bind.annotation.PathVariable;
  10. import org.springframework.web.bind.annotation.RestController;
  11. import org.springframework.web.client.RestTemplate;
  12.  
  13. import java.util.List;
  14.  
  15. /**
  16. * Created by yangjunming on 2017/6/17.
  17. */
  18. @RestController
  19. public class OrderController {
  20.  
  21. @Autowired
  22. private RestTemplate restTemplate;
  23.  
  24. @Autowired
  25. private LoadBalancerClient loadBalancerClient;
  26.  
  27. @Autowired
  28. private DiscoveryClient discoveryClient;
  29.  
  30. @GetMapping("/order/{userId}/{orderNo}")
  31. public String findOrder(@PathVariable Integer userId, @PathVariable String orderNo) {
  32. UserDTO user = restTemplate.getForEntity("http://SERVICE-PROVIDER-DEMO/user/" + userId, UserDTO.class).getBody();
  33. if (user != null) {
  34. return user.getUserName() + " 的订单" + orderNo + " 找到啦!";
  35. }
  36.  
  37. return "用户不存在!";
  38. }
  39.  
  40. @GetMapping("/user-instance")
  41. public List<ServiceInstance> showInfo() {
  42. return this.discoveryClient.getInstances("SERVICE-PROVIDER-DEMO");
  43. }
  44.  
  45. @GetMapping("/log-instance")
  46. public ServiceInstance chooseInstance() {
  47. return this.loadBalancerClient.choose("SERVICE-PROVIDER-DEMO");
  48. }
  49.  
  50. }

这里暴露了3个url,一个个来看:  
a. /order/{userId}/{orderNo} 这个用来示例如何调用service-provider中的方法,注意这里我们并没有用http://localhost:8001/user/1 来调用,而通过http://service-provider-demo/user/ 指定service-provider的application name,让系统从注册中心去发现服务。

b. /user-instance , /log-instance 这二个url 用来辅助输出从注册中心发现的服务实例相关的信息,并非必须。

这里面还有二个注入的实例:restTemplate 、loadBalancerClient ,分别用来发起rest的http请求,以及使用负载均衡从可用的服务列表中,挑出一个可用实例。

5.3 main入口

  1. package com.cnblogs.yjmyzz.spring.cloud.study.service;
  2.  
  3. import org.springframework.boot.SpringApplication;
  4. import org.springframework.boot.autoconfigure.SpringBootApplication;
  5. import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
  6. import org.springframework.cloud.client.loadbalancer.LoadBalanced;
  7. import org.springframework.context.annotation.Bean;
  8. import org.springframework.web.client.RestTemplate;
  9.  
  10. @EnableDiscoveryClient
  11. @SpringBootApplication
  12. public class ServiceConsumer {
  13.  
  14. @Bean
  15. @LoadBalanced
  16. RestTemplate restTemplate() {
  17. return new RestTemplate();
  18. }
  19.  
  20. public static void main(String[] args) {
  21. SpringApplication.run(ServiceConsumer.class, args);
  22. }
  23. }  

依然靠二个关键的注解:@EnableDiscoveryClient、@LoadBalanced,特别是@LoadBalanced,经过这个修饰的restTemplate,就不是普通的restTemplate了,而是具备负载均衡能力的restTemplate,即每次都会用负载均衡算法,从可用服务列表中,挑一个进行调用。

5.3 启动

可以从eukera中看到,service-provider与service-consumer都注册进来了。

调用一下试试:http://localhost:8002/order/1/1000,成功的话会看到下面的输出

注:此时可以把注册中心eureka server停掉,然后再调用下http://localhost:8002/order/1/1000,会发现仍然可以正常调用,说明注册中心的服务列表,在本机是有缓存的,这跟dubbo/dubbox类似。

另外还可以验证下负载均衡,方法如下:

先把service-provider启2个,开二个终端窗口:

java -jar xxx.jar --server.port=9001

java -jar xxx.jar --server.port=9002

这样就能跑二个应用起来,然后看注册中心

然后再调用下consumer的log-instance

可以看到,这次选择的是9002端口应对的实例,然后再刷新一下:

这回选择的是另一个端口9001的实例,说明负载均衡确实起作用了。

至此,一个最基本的SOA框架雏形搭建起来了,当然还有很多地方需要完善,比如:注册中心如何做到HA,服务融断如何处理,注册中心如何安全认证(防止其它服务乱注册)等等,后面再讲。

附:文中示例源码 https://github.com/yjmyzz/spring-cloud-demo

spring cloud 学习(1) - 基本的SOA示例的更多相关文章

  1. Spring Cloud学习(一):Eureka服务注册与发现

    1.Eureka是什么 Eureka是Netflix开发的服务发现框架,本身是一个基于REST的服务,主要用于定位运行在AWS域中的中间层服务,以达到负载均衡和中间层服务故障转移的目的. Eureka ...

  2. spring cloud 学习(9) - turbine stream无法在eureka注册的解决办法

    turbine是啥就不多解释了,初次接触的可以移步spring cloud 学习(4) - hystrix 服务熔断处理 拉到最后看一下,turbine stream默认情况下启动成功后,eureka ...

  3. spring cloud 学习资料

    spring cloud 学习资料 网址 拜托!面试请不要再问我Spring Cloud底层原理 https://mp.weixin.qq.com/s/ZH-3JK90mhnJPfdsYH2yDA

  4. Spring Boot和Spring Cloud学习资源推荐

    Spring Boot和Spring Cloud学习资源推荐   比较好的学习资源,分享一下. 1.Spring Boot官方文档:http://projects.spring.io/spring-b ...

  5. Spring Cloud 学习 之 Spring Cloud Eureka(源码分析)

    Spring Cloud 学习 之 Spring Cloud Eureka(源码分析) Spring Boot版本:2.1.4.RELEASE Spring Cloud版本:Greenwich.SR1 ...

  6. Spring Cloud学习之-什么是Spring Cloud?

    SpringCloud 什么是微服务? 要想学习微服务,首先需要知道什么是微服务?为什么会有微服务?相信看完架构的发展史读者就会明白 架构发展史 单体应用架构 如图所示:将所有的模块,所有内容(页面. ...

  7. Spring Cloud 学习笔记(二)——Netflix

    4 Spring Cloud Netflix Spring Cloud 通过自动配置和绑定到Spring环境和其他Spring编程模型惯例,为Spring Boot应用程序提供Netflix OSS集 ...

  8. Spring Cloud 学习笔记(一)——入门、特征、配置

    [TOC] 0 放在前面 0.1 参考文档 http://cloud.spring.io/spring-cloud-static/Brixton.SR7/ https://springcloud.cc ...

  9. Spring Cloud学习笔记-008

    继承特性 通过上节的示例实践,当使用Spring MVC的注解来绑定服务接口时,几乎完全可以从服务提供方的Controller中依靠复制操作,构建出相应的服务客户端绑定接口.既然存在这么多复制操作,自 ...

随机推荐

  1. AngularJS入门基础——过滤器

    在HTML中的模板绑定符号{{ }}内通过 | 符号来调用过滤器 {{ name | uppercase }}   以HTML的形式使用过滤器时,如果需要传递参数给过滤器,只要在过滤器名字后面加冒号即 ...

  2. SpringSecurity csrf验证忽略某些请求

    前几天项目中遇到springSecurity问题,研究了大半天,掉进了csrf的坑,先认识一下csrf CSRF概念:CSRF跨站点请求伪造(Cross—Site Request Forgery),跟 ...

  3. 20155303 2016-2017-2 《Java程序设计》第六周学习总结

    20155303 2016-2017-2 <Java程序设计>第六周学习总结 课堂笔记 高效学习法推荐 看视频学习(2h)→ 以代码为中心看课本,思考运行结果并验证(3h)→ 课后作业验证 ...

  4. tensorflow随机张量创建

    TensorFlow 有几个操作用来创建不同分布的随机张量.注意随机操作是有状态的,并在每次评估时创建新的随机值. 下面是一些相关的函数的介绍: tf.random_normal 从正态分布中输出随机 ...

  5. springboot:mybatis多数据源配置

    1.application.properties #CMS数据源(主库) spring.datasource.cms.driver-class-name=com.mysql.jdbc.Driver s ...

  6. XShell 使用方法

    XShell是一款Windows下非常优秀的远程连接Linux主机的工具,是平常使用不可缺少的工具.复制和粘贴由于在linux的Shell下,Ctrl+c是中断当前指令,这个快捷键和win系统下的复制 ...

  7. poj1292

    prim,把每个墙看成一个节点,从起点用prim求最小生成树,直到覆盖到终点为止,输出最小生成树中的最大边 #include <cstdio> #include <cmath> ...

  8. (转)js函数前加分号和感叹号是什么意思?有什么用?

    转载地址:https://www.cnblogs.com/mq0036/p/4605255.html 一般看JQuery插件里的写法是这样的 (function($) { //... })(jQuer ...

  9. java 异常 throw

    throw UnsupportedOperationException(); //没有支持的操作NoSuchElementException(); //没有这样的元素

  10. Codeforces 1028E Restore Array 构造

    我发现我构造题真的不会写, 想了好久才想出来.. 我们先把n = 2, 所有数字相等, 所有数字等于0的都特判掉. 找到一个b[ i ] > b[ i - 1 ]的位置把它移到最后一个位置, 并 ...