微服务端点之间的通信(2)— 同步通信下篇

猪小花1号2018-12-21 17:10
3.3.3 财务服务
继续扩展我们的信用评分应用,我们需要添加一个名为FinancialService的微服务。在第2章中,我们创建了一个UserService微服务,用于存储用户的个人信息和地址。在本章中,我们会添加这个财务微服务,用来保存用户的财务详情信息,包括银行账号详情和用户的财务负债情况等。这个微服务的表结构如下:
CREATE TABLE `bank_
account_details` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`user_id` char(36) NOT NULL,
`bank_name` varchar(100) DEFAULT NULL,
`account_number` varchar(20) NOT NULL,
`account_holders_name` varchar(250) NOT NULL,
`fsc` varchar(20) DEFAULT NULL,
`account_type` int(11) DEFAULT NULL,
`created_on` datetime(6) NOT NULL,
`deleted_on` datetime(6) DEFAULT NULL,PRIMARY KEY (`Id`)) ENGINE=InnoDB DEFAULT CHARSET=latin1;
CREATE TABLE
`obligations_details` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`user_id` char(36) NOT NULL,
`total_monthly_spending` decimal(10,0) NOT NULL,
`monthly_income_supports` decimal(10,0) NOT NULL,
`monthly_emi_payout` decimal(10,0) NOT NULL,
`created_on` datetime(6) NOT NULL,
`deleted_on` datetime(6) DEFAULT NULL,
PRIMARY KEY (`Id`),
KEY `Obligations_user_
id` (`user_id`)) ENGINE=InnoDB DEFAULT CHARSET=latin1;
Bootstrap


属性文件中将包含下面这样两个属性:
spring.application.name=financialService
spring.cloud.config.uri=http://localhost:8888
相应地,我们还需要在
configServer
代码中创建
financialService.
properties
文件给财务服务使用的:
server.port=8090
Connect.database=financialDetails
spring.datasource.url=jdbc:mysql://localhost:3306/financialDetails
spring.datasource.username=xxxxxx
spring.datasource.password=xxxxxxxx
# optional Properties
spring.jpa.show-sql = true
spring.jpa.properties.hibernate.dialect = org.hibernate.dialect.MySQL5Dialect
spring.jpa.hibernate.naming
_
strategy = org.hibernate.cfg.ImprovedNamingStrategy


这个应用的创建过程与UserService应用的差不多。需要从start.spring.io下载模板代码。应用的名字可以就叫FinancialServiceApplication。这个服务的主应用文件跟用户服务应用的也差不多是一样的:
package com.practicalMicroservcies;
import org.flywaydb.core.Flyway;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
@SpringBootApplication
public class FinancialServiceApplication {
@Value("${spring.datasource.url}")
private String url;
@Value("${spring.datasource.username}")
private String userName;
@Value("${spring.datasource.password}")
private String password;
@Value("${Connect.database}")
private String database;
@Bean(initMethod = "migrate")
public Flyway flyway() {
String urlWithoutDatabaseName = url.substring(0,url.lastIndexOf("/"));
Flyway flyway = new Flyway();
flyway.setDataSource(urlWithoutDatabaseName, userName, password);
flyway.setSchemas(database);
flyway.setBaselineOnMigrate(true);
return flyway;
}
public static void main(String[] args) {
SpringApplication.run(FinancialServiceApplication.class, args);
}
}


因为这个服务有两张表,相应地我们需要两个实体表,即银行账号详情和债务详情。这些文件里的代码如下:
@Entity
@Table(name = "bank_account_details")
public class BankAccountDetail implements Serializable {
private static final long serialVersionUID = 4804278804874617196L;
@Id
@GeneratedValue(strategy = IDENTITY)
@Column(name = "id")
private int id;
/**
* User Id , unique for every user
*/
@Column(name = "user
_
id", nullable = false, unique = true, length = 36)
private String userId;
/**
* Bank Name of User
*/
@Column(name = "bank_
name", nullable = false, length = 100)
private String bankName;
/**
* Account number of User
*/
@Column(name = "account_number", nullable = false, unique = true,
length = 20)
private String accountNumber;
/**
* Account holder Name of account
*/
@Column(name = "account
_
holders
_
name", nullable = false, length = 250)
private String accountHolderName;
/**
* Any National Financial system code of bank
*/
@Column(name = "fsc", nullable = false, length = 20)
private String fsc;
/**
* Account Type of user
*/
@Column(name = "account
_
type", nullable = false)
private Integer accountType;
/**
* Date on which Entry is created
*/
@Temporal(TemporalType.TIMESTAMP)
@Column(name = "created
_
on", columnDefinition = "TIMESTAMP DEFAULT
CURRENT
_
TIMESTAMP")
private Date createdOn = new Date();
/**
* Date on which Entry is deleted.
*/
@Temporal(TemporalType.TIMESTAMP)
@Column(name = "deleted
_
on", columnDefinition = "TIMESTAMP DEFAULT NULL")
private Date deletedOn;
}


下一个实体类是债务实体,用于存放用户的债务信息,代码如下:
@Entity
@Table(name = "obligations
_
details")
public class ObligationDetails implements Serializable {
/**
**/
private static final long serialVersionUID = -7123033147694699766L;
/**
* unique Id for each record.
同步通信和异步通信
83
@Id
@GeneratedValue(strategy = IDENTITY)
@Column(name = "id")
private int id;
/**
* User Id , unique for every user
*/
@Column(name = "user
_
id", nullable = false, unique = true, length = 36)
private String userId;
/**
* Totally monthly income , may be from himself or wife (combined)
*/
@Column(name = "monthly
_
income
_
supports", nullable = false)
private BigDecimal monthlyIncome;
/**
* Monthly Emi to payout
*/
@Column(name = "monthly
_
emi
_
payout", nullable = false)
private BigDecimal monthlyemi;
/**
* totally month spending
*/
@Column(name = "total
_
monthly
_
spending", nullable = false)
private BigDecimal monthlySpending;
/**
* Date on which Entry is created
*/
@Temporal(TemporalType.TIMESTAMP)
@Column(name = "created
_
on", columnDefinition = "TIMESTAMP DEFAULT
CURRENT
_
TIMESTAMP")
private Date createdOn = new Date();
/**
* Date on which Entry is deleted.
*/
@Temporal(TemporalType.TIMESTAMP)
@Column(name = "deleted
_
on", columnDefinition = "TIMESTAMP DEFAULT NULL")
private Date deletedOn;
}


这个类中还应该有相应的setter和getter方法。
当然,还可以重载toString方法:
@Override
public String toString() {
return "ObligationDetails [userId=" + userId + ",
monthlyIncome=" + monthlyIncome + ", monthlyemi=" + monthlyemi
+ ", monthlySpending=" + monthlySpending + ", createdOn=" +
createdOn + ", deletedOn=" + deletedOn + "]";
}


在用户服务中会有与上面提到的两个实体(银行账号详情和债务详情)对应的代码库类:
package com.practicalMicroservcies.repo;
import org.springframework.data.jpa.repository.JpaRepository;
import com.practicalMicroservcies.entity.BankAccountDetail;
public interface BankAccountDetailRepository extends
JpaRepository<BankAccountDetail, Integer> {
BankAccountDetail findByUserId(String userId);
}


债务实体相关的代码库:
package com.practicalMicroservcies.repo;
import org.springframework.data.jpa.repository.JpaRepository;
import com.practicalMicroservcies.entity.BankAccountDetail;
import com.practicalMicroservcies.entity.ObligationDetails;
public interface ObligationRepository extends
JpaRepository<ObligationDetails, Integer> {
ObligationDetails findByUserId(String userId);
}


还有一个服务类名为
FinancialServices
,用于处理来自两个代码库的一些
查询:
@Service
@Transactional
public class FinancialServices {
@Resource
BankAccountDetailRepository accountDetailRepo;
@Resource
ObligationRepository obligationRepo;


下面的方法是用来保存用户的银行账号详情的:
public void saveAccountDetail(BankAccountDetail accountDetail) {
accountDetailRepo.save(accountDetail);
System.out.println("AccountDetails Saved!");
}
public void saveObligation(ObligationDetails obligationDetails) {
obligationRepo.save(obligationDetails);
System.out.println("Obligation Details Saved!");
}


还需要方法根据提供的
userId
来从数据库提取银行账号详情和债务详情。下面是
该服务类中的两个方法,用来完成获取数据的工作:
public ObligationDetails getObligationDetail(UUID userId) {
ObligationDetails returnAddressObject =
obligationRepo.findByUserId(userId.toString());
return returnAddressObject;
}
public BankAccountDetail getAccountDetail(UUID userId) {
BankAccountDetail userObjectToRetrun =
accountDetailRepo.findByUserId(userId.toString());
return userObjectToRetrun;
}


下面是删除用户财务详情的方法。这个方法并不会真的从数据库里删除数据,而只是
将数据标记为已删除。事实上,对于数据库,所有彻底删除数据的语句都应该尽量避免:
public void deleteFinancialDetail(UUID userId) {
BankAccountDetail accountObject =
accountDetailRepo.findByUserId(userId.toString());
accountObject.setDeletedOn(new Date());
accountDetailRepo.saveAndFlush(accountObject);
ObligationDetails obligationObject =
obligationRepo.findByUserId(userId.toString());
obligationObject.setDeletedOn(new Date());
obligationRepo.saveAndFlush(obligationObject);
}
}


控制器如下所示:
@RestController
@RequestMapping("/PM/finance/")
public class FinancialController {
private static final Logger logger =
Logger.getLogger(FinancialController.class);
@Resource
FinancialServices financialService;
@Resource
ObjectMapper mapper;
/**
* Method is responsible for adding new AccountDetails.
*
* @param address
* @param userId
* @return
*/
public static final String addAccountDetails =
"addAccountDetails(): ";
@RequestMapping(method = RequestMethod.POST, value = "
{userId}/account", produces = "application/json", consumes =
"application/json")
public ResponseEntity<String> addAccountDetails(@RequestBody
BankAccountDetail accountDetail, @PathVariable("userId") UUID
userId) {
logger.debug(addAccountDetails + " Account for user Id " +
userId + " is creating.");
accountDetail.setUserId(userId.toString());
financialService.saveAccountDetail(accountDetail);
return new ResponseEntity<>(HttpStatus.CREATED);
}
/**
* Method is responsible for creating a obligation Details.
*
* @param userDetail
* @param userId
* @return
*/
public static final String addObligationDetails =
"addObligationDetails(): ";
@RequestMapping(method = RequestMethod.POST, value =
"{userId}/obligation", produces = "application/json", consumes =
"application/json")
public ResponseEntity<String> addObligationDetails(@RequestBody
ObligationDetails obligationDetails, @PathVariable("userId") UUID
userId) {
logger.debug(addObligationDetails + " Creating user's
obligation with Id " + userId + " and details : " + obligationDetails);
obligationDetails.setUserId(userId.toString());
financialService.saveObligation(obligationDetails);
return new ResponseEntity<>(HttpStatus.CREATED);
}
/**
* Deleting Financial Detail of user
* @param userDetail
* @param userId
* @return
*/
public static final String deleteFinancialDetails =
"deleteFinancialDetails(): ";
@RequestMapping(method = RequestMethod.DELETE, value = "{userId}",
produces = "application/json", consumes = "application/json")
public ResponseEntity<String> deleteFinancialDetails(
@PathVariable("userId") UUID userId) {
logger.debug(deleteFinancialDetails + " deleting user with Id "
+ userId);
financialService.deleteFinancialDetail(userId);
return new ResponseEntity<>(HttpStatus.CREATED);
}
/**
* Method is responsible for getting the account detail for given ID.
*
* @param userId
* @return
*/
public static final String getAccountDetails =
"getAccountDetails(): ";
@RequestMapping(method = RequestMethod.GET, value =
"{userId}/account", produces = "application/json", consumes =
"application/json")
public ResponseEntity<BankAccountDetail>
getAccountDetails(@PathVariable("userId") UUID userId) {
logger.debug(getAccountDetails + " getting information for userId " +
userId);
BankAccountDetail objectToReturn =
financialService.getAccountDetail(userId);
if (objectToReturn == null)
return new ResponseEntity<>(HttpStatus.NOT
_
FOUND);
else
return new ResponseEntity<>(objectToReturn, HttpStatus.OK);
}
/**
* Method is responsible getting the Obligation Detail.
* @param userId
* @return
*/
public static final String getObligationDetails =
"getObligationDetails(): ";
@RequestMapping(method = RequestMethod.GET, value = "
{userId}/obligation", produces = "application/json", consumes =
"application/json")
public ResponseEntity<ObligationDetails>
getObligationDetails(@PathVariable("userId") UUID userId) {
logger.debug(getObligationDetails + " getting Obligation
Details for user Id: " + userId);
ObligationDetails objectToReturn =
financialService.getObligationDetail(userId);
if (objectToReturn == null)
return new ResponseEntity<>(HttpStatus.NOT
_
FOUND);
else
return new ResponseEntity<>(objectToReturn, HttpStatus.OK);
}
}


至此,我们就完成了财务微服务的所有代码。该服务的端点URL如下所示。对于创建债务,端点URL是:
POST http://localhost:8090/PM/finance/<user Id>/obligation
这个URL的例子如下:
POSThttp://localhost:8090/PM/finance/93a52ce4-9331-43b5-8b56-09bd62cb0444/obligation
对于创建银行账号信息,端点URL是:
POST http://localhost:8090/PM/finance/<user Id>/account
这个URL的例子如下:
POSThttp://localhost:8090/PM/finance/93a52ce4-9331-43b5-8b56-09bd62cb0444/account
如果对统一URL发出GET请求,会从数据库中提取用户的详情信息,然后以JSON格式发回。例如,如果想要知道任何用户的债务信息,可以通过发送GET请求到上面提到的端点URL来获取:GEThttp://localhost:8090/PM/finance/93a52ce4-9331-43b5-8b56-09bd62cb0444/obligation这个API的响应是一个类似这样的JSON数据:
{"id": 1,"user_
id":"3a52ce4-9331-43b5-8b56-09bd62cb0444",
"monthlyIncome":150000,
"monthlyemi":50000,
"monthlySpending":50000,
"createdOn":"1483209000000",
"deletedOn":null
}


该服务用于存储用户的财务信息。它由两张表组成,一张表用来存储用户的银行详情信息,另一张表用来存储用户的月收入、家庭花销、月分期付款EMI花销等。所有这些决定会帮助我们创建信用评分微服务。按照相同的方式,还会有另外一个职业微服务,用于存储职业详情信息,如薪水、是否为个体户、当前雇主是谁以及所有雇员的当前薪水等。这个微服务的实现方式跟现在有的几个微服务非常类似,就留待读者自行实现。


3.4 小结
本章讨论了微服务之间的一些通信原则、各种通信方法(如同步通信和异步通信)以及通信模式(如编制和编排)。同时,还通过一个基于事件的消息队列的例子,讨论了实现基于事件的消息队列的多种场景和不同的方式。REST可以用在同步和异步两种通信类型中。这两种通信方式都有一些对应的工具。在选择合适的通信方式时最主要的还是要结合使用场景和具体项目的需要。第4章将会继续以信用评分微服务为例讲解微服务中的安全问题。


原文网址:https://www.epubit.com/book/detail/27566
内容来源:异步社区;版权属【人民邮电出版社 异步社区】所有,转载已获得授权;未经授权,不得以任何方式复制和传播本书内容,如需转载请联系异步社区。