springcloud通过zuul来实现路由网关服务的,路由网关是整个springcloud项目的门户所在,他能提供一个统一的入口供客户端访问,他涉及到的的服务模块众多,他在远程服务调用中担当服务消费者的角色,客户端的请求到达网关,网关会代理到别的服务上,由别的服务模块完成相应的功能。
在springcloud入门这个系列项目中,springcloud接收两个请求,一个是保存用户信息,即save(name),他会调用feign服务提供的save(name)方法,另一个请求是获取一个message,即get()方法,他会调用ribbon服务提供的get()方法。
网关项目gateway需要通过feign和ribbon的方式调用远程服务,因此他需要依赖spring-cloud-starter-openfeign和spring-cloud-starter-netflix-ribbon,他还需要实现熔断策略,因此还需要依赖spring-cloud-starter-netflix-hystrix,作为网关他需要依赖spring-cloud-starter-netflix-zuul,他也需要作为eureka client注册到服务注册中心,因此需要依赖spring-cloud-starter-netflix-eureka-client。
gateway项目依赖配置:
org.springframework.cloud
spring-cloud-starter
2.0.1.RELEASE
org.springframework.cloud
spring-cloud-starter-netflix-eureka-client
2.0.1.RELEASE
org.springframework.cloud
spring-cloud-starter-netflix-zuul
2.0.1.RELEASE
org.springframework.cloud
spring-cloud-starter-netflix-hystrix
2.0.1.RELEASE
org.springframework.cloud
spring-cloud-starter-netflix-ribbon
2.0.1.RELEASE
org.springframework.cloud
spring-cloud-starter-openfeign
2.0.1.RELEASE
org.webjars
jquery
3.3.1
依赖中加入jquery,是为了在前端页面中使用jquery,这里网关项目提供网关,也提供UI。
为了操作用户,我们定义User实体,和前面feign项目中用到的User实体是一样的。
package com.xxx.gateway.domain;
import java.util.Date;
public class User {
private Integer id;
private String name;
private String mobile;
private Date birth;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getMobile() {
return mobile;
}
public void setMobile(String mobile) {
this.mobile = mobile;
}
public Date getBirth() {
return birth;
}
public void setBirth(Date birth) {
this.birth = birth;
}
public User(){}
public User(String name){
this.name = name;
this.mobile = "";
this.birth = new Date();
}
}
这里只是控制层操作User,因此无需指定表名、主键,也无需生成构造方法,使用默认构造方法。
这里重点是service层的几个类和方法:
首先是RibbonService.java
package com.xxx.gateway.service;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;
@Service
public class RibbonService {
@Autowired
private RestTemplate restTemplate;
@HystrixCommand(fallbackMethod = "fallbackGet")
public String get(){
return restTemplate.getForObject("http://ribbon/get",String.class);
}
public String fallbackGet(){
return "hystrix service broker.";
}
}
这里就涉及到了ribbon远程服务调用的方式,我们需要一个RestTemplate实例对象,然后需要指定服务地址。这里get()方法也通过hystrix熔断服务指定了当远程服务调用失败的时候,备用方法是fallbackGet()。这里需要注入的RestTemplate对象,我们通过如下方式配置:
package com.xxx.gateway.config;
import org.springframework.boot.web.client.RestTemplateBuilder;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;
@Configuration
public class AppConfig {
@Bean
@LoadBalanced
public RestTemplate restTemplate(RestTemplateBuilder builder){
return builder.build();
}
}
另外一个服务类FeignService.java
package com.xxx.gateway.service;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import com.xxx.gateway.domain.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.List;
@Service
public class FeignService {
@Autowired
private UserService userService;
@HystrixCommand(fallbackMethod = "fallbackSave")
public List save(String name){
return userService.save(name);
}
public List fallbackSave(String name){
List list = new ArrayList<>();
User user = new User("user service broker.");
list.add(user);
return list;
}
}
它也通过hystrix熔断机制,指定了调用远程方法失败时候的备选方法fallbackSave(String name),这里备选方法也需要带上和主方法一样的参数。另外这里UserService就是我们通过feign远程调用服务的方式来调用feign项目提供的服务的。
package com.xxx.gateway.service;
import com.xxx.gateway.domain.User;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
import java.util.List;
@FeignClient("feign")
public interface UserService {
@RequestMapping(method= RequestMethod.POST, value = "/save",
produces = MediaType.APPLICATION_JSON_VALUE,
consumes = MediaType.APPLICATION_JSON_VALUE)
@ResponseBody
public List save(@RequestBody String name);
}
UserService是一个接口,只需要通过@FeignClient("feign")注解就可以实现远程服务调用,并且调用时需要通过@RequestMapping()来指定请求的方法。
控制层的方法就很简单了,直接调用服务层service里对应的类和方法,是一个很普通的controller。
package com.xxx.gateway.controller;
import com.xxx.gateway.domain.User;
import com.xxx.gateway.service.FeignService;
import com.xxx.gateway.service.RibbonService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
@RestController
public class WebController {
@Autowired
private RibbonService ribbonService;
@Autowired
private FeignService feignService;
@RequestMapping(value = "/dispatch")
@ResponseBody
public List sendMessage(@RequestBody String name){
return feignService.save(name);
}
@RequestMapping(value = "/get",produces = {MediaType.TEXT_PLAIN_VALUE})
public String get(){
return ribbonService.get();
}
}
启动类:
package com.xxx.gateway;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.cloud.netflix.zuul.EnableZuulProxy;
import org.springframework.cloud.openfeign.EnableFeignClients;
@SpringBootApplication
@EnableEurekaClient
@EnableCircuitBreaker
@EnableFeignClients
@EnableZuulProxy
public class GatewayApplication {
public static void main(String[] args) {
SpringApplication.run(GatewayApplication.class,args);
}
}
@EnableEurekaClient表示开启eureka client的支持,@EnableCircuitBreaker表示开启熔断机制,@EnableFeignClients表示开启feign远程调用客户端支持,@EnableZuulProxy表示开始网关路由支持。
最后,到了配置文件出场的时候了,也是application.yml
server:
port: 80
和bootstrap.yml
spring:
application:
name: gateway
eureka:
client:
service-url:
defaultZone: http://localhost:8761/eureka/
instance:
non-secure-port: ${server.port:80}
另外我们在resources路径下建立一个static文件夹,存放静态页面文件,里面就一个index.html,项目启动直接就可以访问到。内容如下:
gateway
hello,springcloud
启动gateway项目,没有报错,那么启动成功。可以在服务发现管理页面看到gateway服务也加入了进来:
点击get message按钮,第一次可能会显示ribbon service broker,第二次显示了正确的信息:
在第一个输入框中输入beijing,点击save按钮,返回结果显示user service broker。其实已经存入成功,只不过返回了备选方法的结果。
我们再次输入shanghai,点击save按钮,页面上显示了所有用户名:
这个示例很好的展示了springcloud作为微服务框架给我们带来的体验。 gateway只是一个客户端请求统一入口,真正干活的是feign服务和ribbon服务。到这里一个最简单的springcloud示例就展示了,最后还有一个monitor服务,如果看代码不明白,可以看看springcloud入门系列的其他项目:
springcloud入门之服务发现
springcloud入门之配置服务
springcloud入门之通过feign实现负载均衡
springcloud入门之通过ribbon实现负载均衡
项目源代码:https://github.com/buejee/springcloud