上手spring cloud(二)微应用之间的服务调用

栏目: Java · 发布时间: 5年前

内容简介:以商品下单为例,比如将业务拆分为商品服务和订单服务,订单服务会调用商品服务的库存扣减。单个微服务工程,统一按以下目录编排:我们的微服务工程之间,依赖关系如下:

微应用之间的服务调用

服务调用示例

以商品下单为例,比如将业务拆分为商品服务和订单服务,订单服务会调用商品服务的库存扣减。

单个微服务工程,统一按以下目录编排:

-product
--product-common  商品服务公用对象
--product-client  商品服务客户端,以jar包方式被订单服务依赖
--product-server  商品服务,要注册到Eureka Server,外部通过product-client来调用

我们的微服务工程之间,依赖关系如下:

上手spring cloud(二)微应用之间的服务调用

我们从商品微服务工程开始

外层product初始pom.xml:

<modelVersion>4.0.0</modelVersion>
<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.0.2.RELEASE</version>
    <relativePath/>
</parent>
<groupId>com.hicoview</groupId>
<artifactId>product</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>product</name>
<description>Demo project for Spring Boot</description>
<modules>
    <module>product-common</module>
    <module>product-client</module>
    <module>product-server</module>
</modules>

<properties>
    <java.version>1.8</java.version>
    <spring-cloud.version>Finchley.RELEASE</spring-cloud.version>
    <product-common.version>0.0.1-SNAPSHOT</product-common.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>
        <dependency>
            <groupId>com.hicoview</groupId>
            <artifactId>product-common</artifactId>
            <version>${product-common.version}</version>
        </dependency>
    </dependencies>
</dependencyManagement>

product-common初始pom.xml:

<modelVersion>4.0.0</modelVersion>
<parent>
    <groupId>com.hicoview</groupId>
    <artifactId>product</artifactId>
    <version>0.0.1-SNAPSHOT</version>
</parent>
<artifactId>product-common</artifactId>

<dependencies>
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
    </dependency>
</dependencies>

product-client初始pom.xml:

<modelVersion>4.0.0</modelVersion>
<parent>
    <groupId>com.hicoview</groupId>
    <artifactId>product</artifactId>
    <version>0.0.1-SNAPSHOT</version>
</parent>
<artifactId>product-client</artifactId>

<dependencies>
    <dependency>
        <groupId>com.hicoview</groupId>
        <artifactId>product-common</artifactId>
    </dependency>
</dependencies>

product-server的初始pom.xml:

<modelVersion>4.0.0</modelVersion>
<parent>
    <groupId>com.hicoview</groupId>
    <artifactId>product</artifactId>
    <version>0.0.1-SNAPSHOT</version>
</parent>
<artifactId>product-server</artifactId>

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
    </dependency>
    <dependency>
        <groupId>com.hicoview</groupId>
        <artifactId>product-common</artifactId>
    </dependency>
</dependencies>

<build>
    <plugins>
        <plugin>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-maven-plugin</artifactId>
        </plugin>
    </plugins>
</build>

接下来我们调整product-server工程

商品服务需注册到Eureka Server,添加Eureka Client相关依赖:

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-web</artifactId>
</dependency>

启动类加注解@EnableDiscoveryClient:

package com.hicoview.product;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;

@SpringBootApplication
@EnableDiscoveryClient
public class ProductApplication {

    public static void main(String[] args) {
        SpringApplication.run(ProductApplication.class, args);
    }

}

配置application.yml:

eureka:
  client:
    service-url:
      defaultZone: http://localhost:8761/eureka/
spring:
  application:
    name: product
server:
  port: 8080

启动product-server,访问注册中心 http://localhost:8761 ,PRODUCT注册上去了,再继续往下看。

我们写个简单的服务调用示例一下,订单服务下单逻辑调用商品服务进行扣减库存。

继续修改product-server工程

package com.hicoview.product.service;

import java.util.List;

public interface ProductService {
    // 扣减库存
    void decreaseStock();
}
package com.hicoview.product.service.impl;

import com.hicoview.product.service.ProductService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

@Service
@Slf4j
public class ProductServiceImpl implements ProductService {

    @Override
    public void decreaseStock() {
        log.info("------扣减库存-----");
    }
}

spring cloud的RPC服务是使用HTTP方式调用的,所以还要创建ProductController:

package com.hicoview.product.controller;

import com.hicoview.product.service.ProductService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/product")
public class ProductController {

    @Autowired
    private ProductService productService;

    @PostMapping("/decreaseStock")
    public void decreaseStock() {
        productService.decreaseStock();
    }

}

接下来修改product-client工程

pom.xml增加Feign依赖(用于应用间通信):

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>

创建ProductClient:

package com.hicoview.product.client;

import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;

@FeignClient(name="product")
public interface ProductClient {

    @PostMapping("/product/decreaseStock")
    void decreaseStock();

}

再次启动product-server,没问题的话,把product上传到本地maven仓库:

mvn -Dmaven.test.skip=true -U clean install

然后初始化订单微服务工程后继续。

修改order-server工程

order-server的服务可能会被User等其他服务调用,也是个Eureka client,添加依赖:

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-web</artifactId>
</dependency>

启动类加注解@EnableDiscoveryClient

package com.hicoview.product;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;

@SpringBootApplication
@EnableDiscoveryClient
public class ProductApplication {

    public static void main(String[] args) {
        SpringApplication.run(ProductApplication.class, args);
    }

}

配置application.yml:

eureka:
  client:
    service-url:
      defaultZone: http://localhost:8761/eureka/
spring:
  application:
    name: order
server:
  port: 8081

启动order-server,访问 http://localhost:8761/

上手spring cloud(二)微应用之间的服务调用

ORDER服务注册成功后,继续调整order-server工程

由于订单服务要调用商品服务,需添加对product-client依。修改pom.xml:

<dependency>
    <groupId>com.hicoview</groupId>
    <artifactId>product-client</artifactId>
</dependency>

对版本的管理统一交给上层,修改上层order的pom.xml,增加以下配置:

<properties>
    ...
    <product-client.version>0.0.1-SNAPSHOT</product-client.version>
</properties>

<dependencyManagement>
    <dependencies>
        ...
        <dependency>
            <groupId>com.hicoview</groupId>
            <artifactId>product-client</artifactId>
            <version>${product-client.version}</version>
        </dependency>
    </dependencies>
</dependencyManagement>

回到order-server,修改启动类,添加Feign扫描路径:

package com.hicoview.order;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.openfeign.EnableFeignClients;

@SpringBootApplication
@EnableDiscoveryClient
@EnableFeignClients(basePackages = "com.hicoview.product.client")
public class OrderApplication {

    public static void main(String[] args) {
        SpringApplication.run(OrderApplication.class, args);
    }

}

然后按顺序创建Controller、Service:

package com.hicoview.order.controller;

import com.hicoview.order.service.OrderService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/order")
public class OrderController {

    @Autowired
    private OrderService orderService;

    // 创建订单
    @PostMapping("/create")
    public void create() {
        orderService.createOrder();
    }

}
public interface OrderService {
    void createOrder();
}
@Service
@Slf4j
public class OrderServiceImpl implements OrderService {

    @Autowired
    private ProductClient productClient;

    @Override
    public void createOrder() {
        log.info("------创建订单-----");
        // 调用商品扣减服务
        productClient.decreaseStock();
    }
}

启动order-server,发起下单订单请求:

curl -X POST http://localhost:8081/order/create

服务调用成功,order-server和product-server输出:

2019-02-26 11:09:58.408  INFO 3021 --- [nio-8081-exec-3] c.h.order.service.impl.OrderServiceImpl  : ------创建订单-----
2019-02-26 11:09:58.430  INFO 3015 --- [nio-8080-exec-3] c.h.p.service.impl.ProductServiceImpl    : ------扣减库存-----

为了示例的极简,上面服务调用没有涉及到入参和返回值。如果需要定义参数或返回值,考虑到内外部都会用到,需将参数bean定义在product-common中作为公共bean。

Feign和RestTemplate

Rest服务的调用,可以使用Feign或RestTemplate来完成,上面示例我们使用了Feign。

Feign(推荐)

Feign是一个声明式的Web Service客户端,它的目的就是让Web Service调用更加简单。Feign提供了HTTP请求的模板,通过编写简单的接口和插入注解,就可以定义好HTTP请求的参数、格式、地址等信息。

Feign会完全代理HTTP请求,我们只需要像调用方法一样调用它就可以完成服务请求及相关处理。Feign整合了Ribbon和Hystrix(关于Hystrix我们后面再讲),可以让我们不再需要显式地使用这两个组件。

RestTemplate

RestTemplate提供了多种便捷访问远程Http服务的方法,可以了解下。

第一种方式,直接使用RestTemplate,URL写死:

// 1. 
RestTemplate restTemplate = new RestTemplate();
restTemplate.getForObject("http://localhost:8080/product/decreaseStock", String.class);

这种方式直接使用目标的IP,而线上部署的IP地址可能会切换,同一个服务还会启多个进程,所以有弊端。

第二种方式,利用LoadBalancerClient,通过应用名获取URL,然后再使用RestTemplate:

@Autowired
private LoadBalancerClient loadBalancerClient;
// 1. 第二种方式,通过应用名字拿到其中任意一个host和port
ServiceInstance serviceInstance = loadBalancerClient.choose("PRODUCT");
String url = String.format("http://%s:%s", serviceInstance.getHost(), serviceInstance.getPort() + "/product/decreaseStock");
RestTemplate restTemplate = new RestTemplate();
restTemplate.getForObject(url, String.class);

第三种方式,写一个config把RestTemplate作为一个bean配置上去:

@Component
public class RestTemplateConfig {
    @Bean
    @LoadBalanced
    public RestTemplate restTemplate() {
        return new RestTemplate();
    }
}
@Autowired
private RestTemplate restTemplate;
// 使用时url里直接用应用名PRODUCT即可
restTemplate.getForObject("http://PRODUCT/product/decreaseStock", String.class);

Ribbon客户端负载均衡器

Spring Cloud Ribbon是一个基于HTTP和TCP的客户端负载均衡工具,它基于Netflix Ribbon实现。通过Spring Cloud的封装,可以让我们轻松地将面向服务的REST模版请求自动转换成客户端负载均衡的服务调用。Spring Cloud Ribbon虽然只是一个 工具 类框架,它不像服务注册中心、配置中心、API网关那样需要独立部署,但是它几乎存在于每一个Spring Cloud构建的微服务和基础设施中。

在Spring Cloud中,Ribbon可自动从Eureka Server获取服务提供者地址列表,并基于负载均衡算法,请求其中一个服务提供者实例。还有微服务之间的调用,通过Feign或RestTemplate找到一个目标服务。以及API网关Zuul的请求转发等内容,实际上都是通过Ribbon来实现的。


以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

算法学

算法学

哈雷尔 / 第1版 (2006年2月1日) / 2006年2月1日 / 38.0

本书的意图在于按序学习或研究,而不是作为一个参考。因而按照每章依赖于前面章节的结构组织本书,且流畅易读。第一部分预备知识中的大部分材料对于那些具有程序设计背景的人是熟悉的。无论是否恰当,本书包含了计算机科学家当前感兴趣的研究专题的简明讨论。这本教科书的书后有每章详细参考书目的注记,并通过“后向”指针把教科书中的讨论与相关文献联系起来。目前的版本包含大量习题,以及大约三分之一的题解。可用题解作为教科......一起来看看 《算法学》 这本书的介绍吧!

HTML 压缩/解压工具
HTML 压缩/解压工具

在线压缩/解压 HTML 代码

随机密码生成器
随机密码生成器

多种字符组合密码

Base64 编码/解码
Base64 编码/解码

Base64 编码/解码