创建父工程
添加pom依赖
<!--公共的一些配置-->
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
</properties>
<!--1.管理 SpringBoot的jar包-->
<!--SpringBoot-->
<parent>
<groupId> org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.5.RELEASE</version>
</parent>
<!--2.管理 SpringCloud的jar包
-->
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Hoxton.SR3</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<!--3.这里是所有子项目都可以用的jar包-->
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
</dependencies>
创建子工程
创建EurekaServer
添加pom依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- eurekaServer-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
主类添加注解@EnableEurekaServer
@EnableEurekaServer
@SpringBootApplication
publicclassEurekaServerApplication10010 {
publicstaticvoidmain(String[] args) {
SpringApplication.run(EurekaServerApplication10010.class);
}
}
yml配置
server:
port: 10010
eureka:
instance:
hostname: localhost #主机
client:
registerWithEureka: false #关闭自我注册
fetchRegistry: false # 禁止获取注册表
serviceUrl:
defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
server:
enable-self-preservation: false #关闭自我保护
创建EurekaClient
添加pom依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- ereka客户端-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
添加注解@EnableEurekaClient
@SpringBootApplication
@EnableEurekaClient
publicclassUserApplication10020 {
publicstaticvoidmain(String[] args) {
SpringApplication.run(UserApplication10020.class);
}
}
yml配置
server:
port: 10020
eureka:
client:
serviceUrl:
defaultZone: http://localhost:10010/eureka/
instance:
prefer-ip-address: true
instance-id: user-server:10020
spring:
application:
name: user-server
创建一个公共模块
多个EurekaClient都要用到的东西可以放在公共模块中
如pom依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.22</version>
<scope>compile</scope>
</dependency>
<!--ereka客户端-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
在其他工程中导入
<dependency>
<groupId>org.qyf</groupId>
<artifactId>Common</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
创建公共实体类
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
long id;
String name;
int age;
String info;
}
获取ip与端口
@Resource
private RestTemplate restTemplate;
@Resource
private DiscoveryClient discoveryClient;
@RequestMapping("/getUser/{id}")
public User getUser(@PathVariable("id") Long id){
// 获取Eureka服务的ip和端口
List<ServiceInstance> instanceList = discoveryClient.getInstances("user-server");//根据服务名获取实例集合
ServiceInstance instanceInfo = instanceList.get(0);
String ip=instanceInfo.getHost();
int port=instanceInfo.getPort();
System.out.println(ip+" ,"+port);
String url="http://"+ip+":"+port+"/user/getUser/"+id;
return restTemplate.getForObject(url,User.class);
}
RestTemplate服务通信
微服务的通信协议主流的有RPC,Http,SpringCloud是基于Http Restful 风格 ,在Java中发起一个Http请求的方式很多,比如 Apache的HttpClient , OKHttp等等 。Spring为我们封装了一个基于Restful的使用非常简单的Http客户端工具 RestTemplate ,我们就用它来实订单服务和用户服务的通信。
配置RestTemplate
直接在EurekaClient主类中配置
@SpringBootApplication
@EnableEurekaClient
public class OrderApplication10030 {
@Bean
public RestTemplate getRestTemplate(){
return new RestTemplate();
}
public static void main(String[] args) {
SpringApplication.run(OrderApplication10030.class);
}
}
创建配置类
@Component
public class RestTemplateConfig {
@Bean
@LoadBalanced
public RestTemplate getRestTemplate(){
return new RestTemplate();
}
}
EurekaClient集群
以User-server为例
复制User-server-10020,重新命名为User-server-10021
记得rename
修改yml配置
启动
集成Ribbon负载均衡
导入依赖
<!--ribbon 负载均衡-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
</dependency>
开启负载均衡@LoadBalanced
@Bean
@LoadBalanced
public RestTemplate getRestTemplate(){
return new RestTemplate();
}
Ribbon负载均衡算法与Template一致,主类或配置类 配置
//负载均衡算法————随机
@Bean
public RandomRule randomRule(){
return new RandomRule();
}
默认轮询算法,此外还有随机、权重、机房、重试算法
测试
@RestController
@RequestMapping("/order")
public class OrderController {
// @Value("${server.port}")
// private int port;
@Resource
private RestTemplate restTemplate;
@Resource
private DiscoveryClient discoveryClient;
@RequestMapping("/getUser/{id}")
public User getUser(@PathVariable("id") Long id) {
// 获取Eureka服务的ip和端口
List<ServiceInstance> instanceList = discoveryClient.getInstances("user-server");//根据服务名获取实例集合
ServiceInstance instanceInfo = instanceList.get(0);
String ip = instanceInfo.getHost();
int port = instanceInfo.getPort();
System.out.println(ip + " ," + port);
// String url="http://"+ip+":"+port+"/user/getUser/"+id; 不在使用拼接的方式,使用服务名调用
String url="http://user-server/user/getUser/"+id;
User user=restTemplate.getForObject(url,User.class);
return user;
}
}
结果
http://192.168.206.1:10030/order/getUser/1
{
"id": 1,
"name": "张三",
"age": 18,
"info": "haha: 10021"
}
{
"id": 1,
"name": "张三",
"age": 18,
"info": "haha: 10020"
}
feign集成
导入依赖
<!-- openFgign-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
主类添加注解@EnableFeignClients
@SpringBootApplication
@EnableEurekaClient
@EnableFeignClients
public class PayApplication10040 {
public static void main(String[] args) {
SpringApplication.run(PayApplication10040.class);
}
}
提供接口IUserFeignClient
@FeignClient(name="user-server")
public interface IUserFeignClient {
@RequestMapping("/user/getUser/{id}")
public User getUser(@PathVariable("id") long id);
}
Controller注入接口@Resource IUserFeignClient iUserFeignClient
@RestController
@RequestMapping("/pay")
public class PayController {
@Resource
IUserFeignClient iUserFeignClient;
@RequestMapping("/getUser/{id}")
public User getUser(@PathVariable("id") long id){
return iUserFeignClient.getUser(id);
}
}
Hystix集成
Ribbon集成Hystix
导入依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
启动类添加注解@EnableHystrix开启熔断支持
@SpringBootApplication
@EnableEurekaClient
@EnableHystrix
public class OrderApplication10030 {
@Bean
@LoadBalanced
public RestTemplate getRestTemplate(){
return new RestTemplate();
}
public static void main(String[] args) {
SpringApplication.run(OrderApplication10030.class);
}
}
controller中的具体的调用方法上添加注解@HystrixCommand(fallbackMethod = "ribbonFallback")
fallbackMethod = "ribbonFallback"中的ribbonFallback是兜底方法
@RestController
@RequestMapping("/order")
public class OrderController {
@Value("${server.port}")
private int port;
@Resource
private RestTemplate restTemplate;
@Resource
private DiscoveryClient discoveryClient;
@RequestMapping("/getUser/{id}")
@HystrixCommand(fallbackMethod = "ribbonFallback")
public User getUser(@PathVariable("id") Long id) {
String url="http://user-server/user/getUser/"+id;
User user=restTemplate.getForObject(url,User.class);
return user;
}
添加兜底方法(降级方法),方法名必须与fallbackMethod = "ribbonFallback"一致,方法参数必须与原方法一致
public User ribbonFallback(@PathVariable("id") long id) {
return new User(id,"error",0,"ribbon hystrix 兜底方法,服务端大概是挂了");
}
测试
http://192.168.206.1:10030/order/getUser/1
getUser服务有10020和10021两个端口
当轮询到10021时,关闭10021服务
{
"id": 1,
"name": "error",
"age": 0,
"info": "ribbon hystrix 兜底方法,服务端大概是挂了10030"
}
fegin集成Hystix
feign已经集成Hystix,不需要额外导包
yml配置
# 开启fegin的熔断
feign:
hystrix:
enabled: true
关联兜底方法fallbackFactory = UserFeginClientFackFactory.class
@FeignClient(name="user-server",fallbackFactory = UserFeginClientFackFactory.class)
public interface IUserFeignClient {
@RequestMapping("/user/getUser/{id}")
public User getUser(@PathVariable("id") long id);
}
创建UserFeginClientFackFactory类
@Component
public class UserFeginClientFackFactory implements FallbackFactory<IUserFeignClient> {
@Override
public IUserFeignClient create(Throwable throwable) {
throwable.printStackTrace();
// 创建实现类
return new IUserFeignClient(){
@Override
public User getUser(long id) {
return new User(id,"熔断",18,"fegin的熔断兜底方法");
}
};
}
}
测试
http://192.168.206.1:10040/pay/getUser/1
getUser服务有10020和10021两个端口
当轮询到10021时,关闭10021服务
{
"id": 1,
"name": "熔断",
"age": 18,
"info": "fegin的熔断兜底方法"
}
Zuul集成
导入依赖
<!-- zull-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-zuul</artifactId>
</dependency>
启动类添加注解@EnableZuulProxy
@SpringBootApplication
@EnableEurekaClient
@EnableZuulProxy
public class ZuulApplication {
public static void main(String[] args) {
SpringApplication.run(ZuulApplication.class);
}
}
自定义过滤器
@Component
public class LoginZuulFilter extends ZuulFilter {
@Override
public String filterType() {
// return FilterConstants.PRE_TYPE;
return "pre";
}
/**
* 过滤器的执行顺序
* 数值越小越先执行
* @return
*/
@Override
public int filterOrder() {
return 0;
}
/**
* 判断当前请求是否需要过滤
* @return true 会执行run
*/
@Override
public boolean shouldFilter() {
//获取请求路径
RequestContext context=RequestContext.getCurrentContext();
String uri = context.getRequest().getRequestURI();
System.out.println(uri);
//登录页面不拦截
if (uri.contains("login")) {
return false;
}
return true;
}
/**
* 核心过滤方法
* @return
* @throws ZuulException
*/
@Override
public Object run() throws ZuulException {
// 判断用户的权限
// 获取请求头里面的token
RequestContext context = RequestContext.getCurrentContext();
String token = context.getRequest().getHeader("token");
// 有则放行
if (StringUtils.isEmpty(token)){
HttpServletResponse response = context.getResponse();
response.setContentType("application/json;charset=utf-8");
PrintWriter writer=null;
try {
writer= response.getWriter();
Map map=new HashMap();
map.put("res","请求失败");
map.put("code",405);
map.put("msg","缺少权限哦");
//JSON转字符串
writer.write(JSON.toJSONString(map));
} catch (IOException e) {
throw new RuntimeException(e);
}finally {
writer.close();
}
// 不在执行其他过滤器
context.setSendZuulResponse(false);
}
return null;
}
}
yml配合
server:
port: 10050
eureka:
client:
serviceUrl:
defaultZone: http://localhost:10010/eureka/
instance:
instance-id: zuul-server:10050 # 实例id
prefer-ip-address: true #开启ip支持
spring:
application:
name: zull-server #配置服务名称
#配置某个服务的负载均衡算法
#ribbon:
logging:
level:
cn.itsource.springboot.feignclient.UserFeignClient: debug
# 开启fegin的熔断
feign:
hystrix:
enabled: true
zuul:
routes:
pay-server-10040: "/p/**" #指定pay-server这个服务使用 /pay路径来访问 - 别名
order-server-10030: "/o/**" #指定order-server这个服务使用 /order路径来访问
prefix: "/apis" #统一访问前缀
ignoredServices: "*" #禁用掉使用浏览器通过服务名的方式访问服务