如何使用Spring Cloud – 简单服务流程(服务发现与API调用)
2017-06-14 10:56
976 查看
在这里还是要介绍以下Spring Cloud整个的工作流程。首先看一下Spring Cloud的工作流程,
Service Consumer -> Proxy Server -> Client(Load Balancer) -> Servie Discovery -> Target Service
这里就是要一个简单Service API调用的流程。下面看一下Spring Cloud中的组件与上面流程的对应关系。
Spring Cloud | Module |
Restful API (Spring Boot) | Target Service |
Eureka | Servie Discovery |
Client & Load Balancer | Spring RestTemplate & Ribbon |
Proxy(Edge) Server | Zuul |
Service Consumer | Browser,Curl,Other Apps,etc |
###数据结构及基础服务:
在这里使用Spring Boot快速的创建三个基础服务,并且其中三者之间存在逻辑依赖关系。
Company Service
Java
public class Company { private Long id; private String name; // Constructor、Getter、Setter } @RestController public class CompanyService { @RequestMapping("/company/{id}") public Company getCompanyById(@PathVariable("id") Long id){ sleep(); return new Company(id, "Company"); } //利用时间等待模拟Serivce调用时长 private void sleep() { Random rand = new Random(); int time = rand.nextInt(2000); try { Thread.sleep(time); } catch (Exception e) { e.printStackTrace(); } } }
public class Company { private Long id; private String name; // Constructor、Getter、Setter } @RestController public class CompanyService { @RequestMapping("/company/{id}") public CompanygetCompanyById(@PathVariable("id") Long id){ sleep(); return new Company(id, "Company"); } //利用时间等待模拟Serivce调用时长 private void sleep() { Randomrand = new Random(); int time = rand.nextInt(2000); try { Thread.sleep(time); } catch (Exception e) { e.printStackTrace(); } } }
Employee Service
Java
public class Employee { private Long id; private Long companyId; private String name; // Constructor、Getter、Setter } @RestController public class EmployeeService { @RequestMapping("/employee/{id}") public Employee getEmployeeById(@PathVariable("id") Long id) { sleep(); return new Employee(id,1L,"张三"); } @RequestMapping("/employee") public List<Employee> getEmployeesByCompanyId(@RequestParam("companyId") Long companyId){ List<Employee> employees = new ArrayList<>(); employees.add(new Employee(1L, companyId, "张三")); employees.add(new Employee(2L, companyId, "李四")); employees.add(new Employee(3L, companyId, "王五")); sleep(); return employees; } private void sleep() { Random rand = new Random(); int time = rand.nextInt(2000); try { Thread.sleep(time); } catch (Exception e) { e.printStackTrace(); } } }
public class Employee { private Long id; private Long companyId; private String name; // Constructor、Getter、Setter } @RestController public class EmployeeService { @RequestMapping("/employee/{id}") public EmployeegetEmployeeById(@PathVariable("id") Long id) { sleep(); return new Employee(id,1L,"张三"); } @RequestMapping("/employee") public List<Employee> getEmployeesByCompanyId(@RequestParam("companyId") Long companyId){ List<Employee> employees = new ArrayList<>(); employees.add(new Employee(1L, companyId, "张三")); employees.add(new Employee(2L, companyId, "李四")); employees.add(new Employee(3L, companyId, "王五")); sleep(); return employees; } private void sleep() { Randomrand = new Random(); int time = rand.nextInt(2000); try { Thread.sleep(time); } catch (Exception e) { e.printStackTrace(); } } }
Product Service
Java
public class Product { private Long id; private Long companyId; private String sku; // Constructor、Getter、Setter } @RestController public class ProductService { private static final Logger LOG = LoggerFactory.getLogger(ProductService.class); @RequestMapping("/product/{id}") public Product getProductById(@PathVariable("id") Long id) { sleep(); return new Product(id, 1L, "T001"); } @RequestMapping("/product") public List<Product> getProductsByCompanyId(@RequestParam("companyId") Long companyId) { List<Product> products = new ArrayList<>(); products.add(new Product(1L, companyId, "T001")); products.add(new Product(2L, companyId, "T002")); products.add(new Product(3L, companyId, "T003")); return products; } private void sleep() { Random rand = new Random(); int time = rand.nextInt(3000); try { Thread.sleep(time); } catch (Exception e) { e.printStackTrace(); } } }
public class Product { private Long id; private Long companyId; private String sku; // Constructor、Getter、Setter } @RestController public class ProductService { private static final LoggerLOG = LoggerFactory.getLogger(ProductService.class); @RequestMapping("/product/{id}") public ProductgetProductById(@PathVariable("id") Long id) { sleep(); return new Product(id, 1L, "T001"); } @RequestMapping("/product") public List<Product> getProductsByCompanyId(@RequestParam("companyId") Long companyId) { List<Product> products = new ArrayList<>(); products.add(new Product(1L, companyId, "T001")); products.add(new Product(2L, companyId, "T002")); products.add(new Product(3L, companyId, "T003")); return products; } private void sleep() { Randomrand = new Random(); int time = rand.nextInt(3000); try { Thread.sleep(time); } catch (Exception e) { e.printStackTrace(); } } }
这三个服务都应该是正常独立服务的。
所有的服务都需要在注册中心进行对当前服务的注册,因此要在三个服务的启动程序上加上注解 @EnableDiscoveryClient
在配置文件bootstrap.yml中加入以下配置
YAML
spring: application: name: company-service cloud: config: uri: ${vcap.services.${PREFIX:}configserver.credentials.uri:http://user:password@localhost:8888}
spring: application: name: company-service cloud: config: uri: ${vcap.services.${PREFIX:}configserver.credentials.uri:http://user:password@localhost:8888}
###服务中心
注册中心,在Demo中只用到最简单的服务发现与注册,只需要在启动程序加上注解 @EnableEurekaServer
@EnableDiscoveryClient
在application.yml中对注册服务进行配置
YAML
server: port: 8761 security: user: password: ${eureka.password} eureka: client: registerWithEureka: false fetchRegistry: false server: waitTimeInMsWhenSyncEmpty: 0 password: ${SECURITY_USER_PASSWORD:password}
server: port: 8761 security: user: password: ${eureka.password} eureka: client: registerWithEureka: false fetchRegistry: false server: waitTimeInMsWhenSyncEmpty: 0 password: ${SECURITY_USER_PASSWORD:password}
###远程调用Client Adapter
使用服务发现来寻找真实的服务地址,并获取API返回的数据。
在这里需要创建三个model,用来转换API结构
Java
public class CompanyAll { private Long id; private String name; private List<ProductDetail> productList; private List<EmployeeDetail> employeeList; public CompanyAll(Company company, List<Product> productList, List<Employee> employeeList) { this.id = company.getId(); this.name = company.getName(); if (employeeList != null) { this.productList = productList.stream().map(product -> new ProductDetail(product.getId(), product.getSku()) ).collect(Collectors.toList()); } if (employeeList != null) { this.employeeList = employeeList.stream().map(employee -> new EmployeeDetail(employee.getId(), employee.getName()) ).collect(Collectors.toList()); } } //Getter、Setter } public class EmployeeDetail { private Long id; private String name; // Constructor、Getter、Setter } public class ProductDetail { private Long id; private String sku; // Constructor、Getter、Setter }
public class CompanyAll { private Long id; private String name; private List<ProductDetail> productList; private List<EmployeeDetail> employeeList; public CompanyAll(Companycompany, List<Product> productList, List<Employee> employeeList) { this.id = company.getId(); this.name = company.getName(); if (employeeList != null) { this.productList = productList.stream().map(product -> new ProductDetail(product.getId(), product.getSku()) ).collect(Collectors.toList()); } if (employeeList != null) { this.employeeList = employeeList.stream().map(employee -> new EmployeeDetail(employee.getId(), employee.getName()) ).collect(Collectors.toList()); } } //Getter、Setter } public class EmployeeDetail { private Long id; private String name; // Constructor、Getter、Setter } public class ProductDetail { private Long id; private String sku; // Constructor、Getter、Setter }
创建工具类AppUtil
在工具类中会调用到LoadBalancerClient,这个就是有Netflix Ribbon提供的Client。他会根据ServiceId(配置文件中的Service Name)向Eureka(注册服务器)获取服务地址。在这里也可以提供一个容错机制,极限情况下,全宕机时使用fallbackUri。
Java
@Component public class AppUtil { @Autowired private LoadBalancerClient loadBalancer; public URI getRestUrl(String serviceId, String fallbackUri) { URI uri = null; try { ServiceInstance instance = loadBalancer.choose(serviceId); uri = instance.getUri(); } catch (RuntimeException e) { uri = URI.create(fallbackUri); } return uri; } public <T> ResponseEntity<T> createOkResponse(T body) { return createResponse(body, HttpStatus.OK); } public <T> ResponseEntity<T> createResponse(T body, HttpStatus httpStatus) { return new ResponseEntity<>(body, httpStatus); } public <T> T json2Object(ResponseEntity<String> response, Class<T> clazz) { try { return (T) JSON.parseObject(response.getBody(), clazz); } catch (Exception e) { throw new RuntimeException(e); } } public <T> List<T> json2Objects(ResponseEntity<String> response, Class<T> clazz) { try { return JSON.parseArray(response.getBody(), clazz); } catch (Exception e) { throw new RuntimeException(e); } } }
@Component public class AppUtil { @Autowired private LoadBalancerClientloadBalancer; public URIgetRestUrl(String serviceId, String fallbackUri) { URIuri = null; try { ServiceInstanceinstance = loadBalancer.choose(serviceId); uri = instance.getUri(); } catch (RuntimeException e) { uri = URI.create(fallbackUri); } return uri; } public <T> ResponseEntity<T> createOkResponse(T body) { return createResponse(body, HttpStatus.OK); } public <T> ResponseEntity<T> createResponse(T body, HttpStatushttpStatus) { return new ResponseEntity<>(body, httpStatus); } public <T> T json2Object(ResponseEntity<String> response, Class<T> clazz) { try { return (T) JSON.parseObject(response.getBody(), clazz); } catch (Exception e) { throw new RuntimeException(e); } } public <T> List<T> json2Objects(ResponseEntity<String> response, Class<T> clazz) { try { return JSON.parseArray(response.getBody(), clazz); } catch (Exception e) { throw new RuntimeException(e); } } }
当然这里还需要一个Client来对Api进行访问及Response数据处理
Java
@Component public class RemoteServiceClient { @Autowired AppUtil appUtil; private RestTemplate restTemplate = new RestTemplate(); public ResponseEntity<Company> getCompanyById(Long id) { URI uri = appUtil.getRestUrl("COMPANY-SERVICE", "http://localhost:57773/"); String url = uri.toString() + "/company/" + id; ResponseEntity<String> resultStr = restTemplate.getForEntity(url, String.class); Company company = appUtil.json2Object(resultStr, Company.class); return appUtil.createOkResponse(company); } public ResponseEntity<List<Employee>> getEmployeesByCompanyId(Long id) { try { URI uri = appUtil.getRestUrl("EMPLOYEE-SERVICE", "http://localhost:58017"); String url = uri.toString() + "/employee?companyId=" + id; ResponseEntity<String> resultStr = restTemplate.getForEntity(url, String.class); List<Employee> employees = appUtil.json2Objects(resultStr, Employee.class); return appUtil.createOkResponse(employees); } catch (Throwable t) { throw t; } } public ResponseEntity<List<Product>> getProductsByCompanyId(Long id) { try { URI uri = appUtil.getRestUrl("PRODUCT-SERVICE", "http://localhost:57750"); String url = uri.toString() + "/product?companyId=" + id; ResponseEntity<String> resultStr = restTemplate.getForEntity(url, String.class); List<Product> employees = appUtil.json2Objects(resultStr, Product.class); return appUtil.createOkResponse(employees); } catch (Throwable t) { throw t; } } }
@Component public class RemoteServiceClient { @Autowired AppUtilappUtil; private RestTemplaterestTemplate = new RestTemplate(); public ResponseEntity<Company> getCompanyById(Long id) { URIuri = appUtil.getRestUrl("COMPANY-SERVICE", "http://localhost:57773/"); String url = uri.toString() + "/company/" + id; ResponseEntity<String> resultStr = restTemplate.getForEntity(url, String.class); Companycompany = appUtil.json2Object(resultStr, Company.class); return appUtil.createOkResponse(company); } public ResponseEntity<List<Employee>> getEmployeesByCompanyId(Long id) { try { URIuri = appUtil.getRestUrl("EMPLOYEE-SERVICE", "http://localhost:58017"); String url = uri.toString() + "/employee?companyId=" + id; ResponseEntity<String> resultStr = restTemplate.getForEntity(url, String.class); List<Employee> employees = appUtil.json2Objects(resultStr, Employee.class); return appUtil.createOkResponse(employees); } catch (Throwable t) { throw t; } } public ResponseEntity<List<Product>> getProductsByCompanyId(Long id) { try { URIuri = appUtil.getRestUrl("PRODUCT-SERVICE", "http://localhost:57750"); String url = uri.toString() + "/product?companyId=" + id; ResponseEntity<String> resultStr = restTemplate.getForEntity(url, String.class); List<Product> employees = appUtil.json2Objects(resultStr, Product.class); return appUtil.createOkResponse(employees); } catch (Throwable t) { throw t; } } }
最终还是需要对外暴露一个端口来返回数据,这里也是一个Restful接口
Java
@RestController public class RemoteAdapter { private static final Logger LOG = LoggerFactory.getLogger(RemoteAdapter.class); @Autowired RemoteServiceClient client; @Autowired AppUtil appUtil; @RequestMapping("/") public String welcome() { return "welcome to use my demo"; } @RequestMapping("/company/{id}") public ResponseEntity<CompanyAll> getCompany(@PathVariable("id") Long id) { ResponseEntity<Company> companyResult = client.getCompanyById(id); if (!companyResult.getStatusCode().is2xxSuccessful()) { return appUtil.createResponse(null, companyResult.getStatusCode()); } List<Product> products = null; try { ResponseEntity<List<Product>> productsResult = client.getProductsByCompanyId(id); if (!productsResult.getStatusCode().is2xxSuccessful()) { LOG.error("远程调用Product API 失败"); } else { products = productsResult.getBody(); } } catch (Throwable t) { LOG.error("远程调用Product API 异常 ", t); throw t; } List<Employee> employees = null; try { ResponseEntity<List<Employee>> employeeResult = null; employeeResult = client.getEmployeesByCompanyId(id); if (!employeeResult.getStatusCode().is2xxSuccessful()) { LOG.error("远程调用Employee API 失败"); } else { employees = employeeResult.getBody(); } } catch (Throwable t) { LOG.error("远程调用Employee API 失败"); throw t; } return appUtil.createOkResponse(new CompanyAll(companyResult.getBody(), products, employees)); } }
@RestController public class RemoteAdapter { private static final LoggerLOG = LoggerFactory.getLogger(RemoteAdapter.class); @Autowired RemoteServiceClientclient; @Autowired AppUtilappUtil; @RequestMapping("/") public String welcome() { return "welcome to use my demo"; } @RequestMapping("/company/{id}") public ResponseEntity<CompanyAll> getCompany(@PathVariable("id") Long id) { ResponseEntity<Company> companyResult = client.getCompanyById(id); if (!companyResult.getStatusCode().is2xxSuccessful()) { return appUtil.createResponse(null, companyResult.getStatusCode()); } List<Product> products = null; try { ResponseEntity<List<Product>> productsResult = client.getProductsByCompanyId(id); if (!productsResult.getStatusCode().is2xxSuccessful()) { LOG.error("远程调用Product API 失败"); } else { products = productsResult.getBody(); } } catch (Throwable t) { LOG.error("远程调用Product API 异常 ", t); throw t; } List<Employee> employees = null; try { ResponseEntity<List<Employee>> employeeResult = null; employeeResult = client.getEmployeesByCompanyId(id); if (!employeeResult.getStatusCode().is2xxSuccessful()) { LOG.error("远程调用Employee API 失败"); } else { employees = employeeResult.getBody(); } } catch (Throwable t) { LOG.error("远程调用Employee API 失败"); throw t; } return appUtil.createOkResponse(new CompanyAll(companyResult.getBody(), products, employees)); } }
Ok,这里最后还是需要application.yml配置文件,对相关进行配置。当然bootstrap.yml的注册配置还是不能少的
YAML
server: port: 0 eureka: instance: leaseRenewalIntervalInSeconds: 10 metadataMap: instanceId: ${vcap.application.instance_id:${spring.application.name}:${spring.application.instance_id:${random.value}}} client: registryFetchIntervalSeconds: 5
server: port: 0 eureka: instance: leaseRenewalIntervalInSeconds: 10 metadataMap: instanceId: ${vcap.application.instance_id:${spring.application.name}:${spring.application.instance_id:${random.value}}} client: registryFetchIntervalSeconds: 5
###代理(边界)服务
边界服务在这里也demo中没有运用到太多的功能,这里只使用了路由转发的作用,因此也不用做过多的配置。只需要在启动程序上添加注解 @Controller @EnableZuulProxy即可
在application.yml需要对服务进行一些配置,由于在上述代码中提到了线程等待时间来模拟API调用时间,因此需要对ribbon的TimeOut时间设置到60秒。对zuul的配置最常用的是路由,对Client暴漏的服务接口进行路由转发
YAML
info: component: Zuul Server endpoints: restart: enabled: true shutdown: enabled: true health: sensitive: false hystrix: command: default: execution: timeout: enabled: false ribbon: ReadTimeout: 60000 ConnectTimeout: 6000 zuul: ignoredServices: "*" routes: service-adapter: path: /app/** server: port: 8765 logging: level: ROOT: INFO org.springframework.web: INFO
info: component: Zuul Server endpoints: restart: enabled: true shutdown: enabled: true health: sensitive: false hystrix: command: default: execution: timeout: enabled: false ribbon: ReadTimeout: 60000 ConnectTimeout: 6000 zuul: ignoredServices: "*" routes: service-adapter: path: /app/** server: port: 8765 logging: level: ROOT: INFO org.springframework.web: INFO
## 结果预览
来源:http://www.tuicool.com/articles/VbURJzQ
相关文章推荐
- 如何使用Spring Cloud – 简单服务流程(服务发现与API调用)
- 如何使用Spring Cloud – 简单服务流程
- ArcGIS API for javascript开发笔记(六)——REST详解及如何使用REST API调用GP服务
- ArcGIS API for javascript开发笔记(六)——REST详解及如何使用REST API调用GP服务
- 如何用Google APIs和Google的应用系统进行集成(3)----调用Google 发现(Discovery)API的RESTful服务
- 微信开发使用wechat.class.php调用API,测试发现返回no access,公众号出现暂时无法提供服务
- Fiddler实验:使用Composer发起百度短网址服务API调用请求
- java高级编程学习如何使用打印服务 API
- Flex中如何使用WebService类调用一个简单web service的例子
- 【菜鸟学WCF】使用ScriptManager+Ajax调用WCF服务之如何为服务创建接口
- jni使用基础(八)之jni使用流程及C调用java简单说明
- 如何用javascript api for arcgis调用有参数的GP服务
- 如何在 Web 服务中使用 Service Data Objects 2.1 的动态数据 API
- 【翻译】MSIL 教程(二):数组、分支、循环、使用不安全代码和如何调用Win32 API
- Parse教程——如何简单地使用Parse在iOS应用创建后台服务
- Parse教程——如何简单地使用Parse在iOS应用创建后台服务
- 语义Web服务的API使用实例:OWL-S API结合matchmaker、推理机(Jena、Pellet)进行语义转换、匹配、组合及调用web服务
- Jquery(Ajax) 调用 SharePoint 2013 Search Rest API 并使用Josn反回结果并简单显示
- MSIL 教程(二):数组、分支、循环、使用不安全代码和如何调用Win32 API
- CXF之使用CXF API 发布服务与进行客户端调用