首页 | 新闻 | 新品 | 文库 | 方案 | 视频 | 下载 | 商城 | 开发板 | 数据中心 | 座谈新版 | 培训 | 工具 | 博客 | 论坛 | 百科 | GEC | 活动 | 主题月 | 电子展
返回列表 回复 发帖

使用Spring Cloud连接不同服务(1)

使用Spring Cloud连接不同服务(1)

主要结论
  • Spring Cloud为微服务系统中相互依赖的服务提供了丰富的连接选项。
  • Spring Cloud Config为配置数据提供了通过Git管理的版本控制机制,并能在无需重启动的情况下对此类数据进行动态刷新。
  • 通过将Spring Cloud与Netflix Eureka以及Ribbon组件配合使用,应用程序服务将能用更为动态的方式相互发现,并能通过专用负载平衡器代理将负载平衡决策推送至客户端服务。
  • 系统的边缘位置依然有诸如AWS ELB等负载平衡解决方案的一席之地,这里的传入流量还无法控制。
  • 针对中间层微服务之间的通信,Ribbon提供了一种更为可靠和高性能的解决方案,该方案不依赖特定的云供应商。
简介随着转向基于微服务的体系结构,我们开始面临一项重要决策:如何将不同服务连接在一起?单层系统(Monolithic system)中的不同组件可以通过简单的方法调用进行通信,但微服务系统中的不同组件很有可能需要借助REST、Web服务,或某种类似RPC的机制实现网络通信。
在单层系统中,可以完全避免服的连接方面遇到的问题,让每个组件根据需求创建自己的依存项。但实际上我们很少会这样做。组件和依存项之间的这种紧密耦合会使得系统过于僵硬,会对测试工作产生不利影响。此时我们会选择让组件的依存关系外化(Externalise),并在创建组件时直接注入这样的关系,依存关系的注入主要可用于类和对象的连接。

假设打算通过一系列微服务实现一个应用程序,可以使用与单层系统类似的连接选项。依存项的地址可硬编码到程序中,借此将服务紧密连接在一起。或者也可以将所依赖的服务地址外化,并在部署或运行的时候提供这些服务。本文将介绍在微服务应用程序的构建过程中,如何通过Spring Boot和Spring Cloud实现这些选项。
我们假设了下图所示的一个名为repmax的简单微服务系统:

Repmax系统
Repmax应用程序可以记录追踪用户的举重成绩,并用每次举重前五名选手的成绩生成排行榜。其中logbook服务负责通过UI收集每次练习的数据并存储每位用户的完整历史信息。当用户在练习完毕录入成绩后,logbook会将此次举重的详细信息发送至leaderboard服务。
从图中可以看到,logbook服务需要依赖leaderboard服务。从最佳实践的角度考虑,我们将这个依存项抽象为LeaderBoardApi接口:
public interface LeaderBoardApi {    void recordLift(Lift lift);}
由于这是个Spring应用程序,需要使用RestTemplate处理logbook和leaderboard服务之间通信的细节:
[url=][/url]
1 abstract class AbstractLeaderBoardApi implements LeaderBoardApi { 2     private final RestTemplate restTemplate; 3 4     public AbstractLeaderBoardApi() { 5         RestTemplate restTemplate = new RestTemplate(); 6         restTemplate.getMessageConverters().add(new FormHttpMessageConverter()); 7         this.restTemplate = restTemplate; 8     } 9 10     @Override11     public final void recordLift(Lifter lifter, Lift lift) {12         URI url = URI.create(String.format("%s/lifts", getLeaderBoardAddress()));13 14         MultiValueMap<String, String> params = new LinkedMultiValueMap<>();15         params.set("exerciseName", lift.getDescription());16         params.set("lifterName", lifter.getFullName());17         params.set("reps", Integer.toString(lift.getReps()));18         params.set("weight", Double.toString(lift.getWeight()));19 20         this.restTemplate.postForLocation(url, params);21     }22 23     protected abstract String getLeaderBoardAddress();24 }[url=][/url]

AbstractLeaderBoardApi类可以捕获针对leaderboard服务创建POST请求的全部逻辑,并可通过子类指定leaderboard服务的准确地址。将多个微服务相互连接最简单的方法可能就是将每个服务需要的依存项地址硬编码到程序中。这相当于在单层系统的世界中通过硬编码的方式实现依赖项的具现化(Instantiation)。这一点可以在StaticWiredLeaderBoardApi类中轻松实现:
public class StaticWiredLeaderBoardApi extends AbstractLeaderBoardApi {    @Override    protected String getLeaderBoardAddress() {        return "http://localhost:8082";    }}硬编码方式指定的服务地址使得我们能够快速上手,但在现实环境中这样做有些不太实际。服务的每个不同部署需要自定义编译,这一做法很快会变得充满痛苦并且容易出错。
如果要部署的是单层系统,并且希望对应用程序进行重构以消除硬编码的地址,首先需要将地址信息外化至配置文件。微服务应用程序也可以使用相似的方法:将地址信息推送至配置文件,并让所实现的API从配置中读取地址。
Spring Boot使得配置参数的定义和注入工作变得更简单。只要将地址参数加入application.properties文件即可:
leaderboard.url=http://localhost:8082
随后可以使用@Value标注(Annotation)将这个参数注入ConfigurableLeaderBoardApi:
[url=][/url]
public class ConfigurableLeaderBoardApi extends AbstractLeaderBoardApi {    private final String leaderBoardAddress;    @Autowired    public ConfigurableLeaderBoardApi(@Value("${leaderboard.url}") String leaderBoardAddress) {        this.leaderBoardAddress = leaderBoardAddress;    }    @Override    protected String getLeaderBoardAddress() {        return this.leaderBoardAddress;    }}[url=][/url]


Spring Boot对 的支持使得我们不仅可以通过修改配置文件更改leaderboard.url的值,而且可以在启动应用程序时指定环境变量:
LEADERBOARD_URL=http://repmax.skipjaq.com/leaderboard java -jar repmax-logbook-1.0.0-RELEASE.jar随后即可在不更改代码的情况下将logbook服务实例指向任何一个leaderboard服务实例。如果系统符合 原则,环境中很可能已经包含了连接信息,因此可通过简单的工作将其直接映射至应用程序。
诸如 和 等平台即服务(PaaS)系统会将数据库和消息系统等托管服务的连接信息暴露到环境中,这样即可用完全相同的方式连接这些依赖项。实际上,将两个服务连接在一起,以及将一个服务与相应的数据存储连接在一起,这两种做法并没有什么本质差异,都只是将两个分布式系统连接在一起。
返回列表