springcloud微服务

一、微服务优点:

1.完全耦合:关联紧密,不能分开,下一步以上一步为基础
2. 松耦合:关联度不高
3.完全解耦:没有任何关联
微服务可以放在容器里跑,容器需要给微服务提供环境,容器里跑的就是微服务,容器跟微服务没有关系。

二、主要组件

1.Eureka、Nacos(注册发现): Nacos和Eureka都是注册中心,都具有各自的负载均衡策略。Nacos有自己的配置中心,Eureka需要配合config实现配置中心,且不提供管理界面,nacos是动态刷新的,它采用Netty保持长连接实时推送,eureka需要配合MQ实现配置动态刷新。相比较来说Nacos性能更好,因此现在用的最多的也是Nacos。

2.Feign、Openfeign(远程调用):微服务之间通过rest接口通讯,springcloud提供fegin框架来支持rest的调用,Feign本身不支持Spring MVC的注解,使用Feign的注解定义接口,调用这个接口,就可以调用服务注册中心的服务,openfeign是feign的升级版,OpenFeign的@FeignClient可以解析SpringMVC的@RequestMapping注解下的接口,并通过动态代理的方式产生实现类,实现类中做负载均衡并调用其他服务。

3.Ribbon(负载均衡):Ribbon实现客户端的负载均衡,负载均衡器提供很多对http和tcp的行为控制。Spring cloud Feign已经集成Ribbon,所以注解@FeignClient的类,默认实现了ribbon的功能。

4.Hystrix(熔断器):Hystrix被称为熔断器,它是一个用于处理分布式系统的延迟和容错的开源库,在分布式系统里,许多服务之间通过远程调用实现信息交互,调用时不可避免会出现调用失败,比如超时、异常等原因导致调用失败,Hystrix能够保证在一个服务出问题的情况下,不会导致整体服务失败,避免级联故障(服务雪崩),以提高分布式系统的弹性。

5.Gateway(网关):为微服务框架提供一种简单而有效的统一的 API 路由管理方式,统一访问接口,目前已经替代传统的zuul网关了。

三.代码实现

3.1 首先创建两个模块,实现基本查询功能(基础,不做演示),分为用户服务和订单服务

 3.2  Nacos服务注册发现

windows中去github先下载Nacos:输入网址 nacos.io,前往github下载,点击release,再点击tag选择需要下载的版本。

下载好后去到nacos\bin目录下双击startup,开启nacos

 或者通过cmd去到bin目录下执行startup.cmd-m standalone命令也可以

成功后出现如下:

 这边需要注意,我的这个默认是单点模式stand alone mode, 有些可能默认是cluster mode集群模式,因为我这边测试就是用的单点模式,如有是集群模式需要改一下,还是在bin目录下的startup文件中改就可以。默认端口号是8848

 然后输入localhost:8848/nacos进入nacos页面,用户名密码都是nacos

随后添加相关依赖:

父工程添加springcloud-alibaba依赖

<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-alibaba-dependencies</artifactId>
    <version>2.2.6.RELEASE</version>
    <type>pom</type>
    <scope>import</scope>
</dependency>

然后在userservice和orderservice中分别添加nacos依赖


        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
        </dependency>

        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
        </dependency>

然后在application.yml中配置相关信息

server:
  port: 8081
spring:
  datasource:
    url: jdbc:mysql://localhost:3306/cloud_user?useSSL=false
    username: root
    password: root
    driver-class-name: com.mysql.jdbc.Driver
  application:
    name: userservice  
  cloud:
    nacos:
      server-addr: 127.0.0.1:8848
mybatis:
  type-aliases-package: cn.itcast.user.pojo
  configuration:
    map-underscore-to-camel-case: true
logging:
  level:
    cn.itcast: debug
  pattern:
    dateformat: MM-dd HH:mm:ss:SSS

然后启动项目:随便查一个id数据,可以看到已成功

然后我们去nacos界面查看,可以看到userservice微服务已经成功开启

同理,再去启动orderservice

 至此服务都起来了。

3.3 远程调用

首先传统的我们可以通过restTemplate来进行,首先我们需要创建一个配置文件config,用来创建restTemplate对象。(加入从orderservice中调用userservice)

 

package cn.itcast.order.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;

@Configuration
public class RestTemplateConfig {

    @Bean
    public RestTemplate restTemplate(){
        return new RestTemplate();
    }

}

 然后在orderservice中注入restTemplate, 调用相应的api即可

package cn.itcast.order.service;

import cn.itcast.order.mapper.OrderMapper;
import cn.itcast.order.pojo.Order;
import cn.itcast.order.pojo.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;

import javax.annotation.Resource;

@Service
public class OrderService {

    @Resource
    private OrderMapper orderMapper;

    @Autowired
    private RestTemplate restTemplate;

    public Order queryOrderById(Long orderId) {
        // 1.查询订单
        Order order = orderMapper.findById(orderId);

        //利用resttemplate发起http请求,查询用户信息,这个路径就是userservice的访问路径
        String url = "http://localhost:8081/user/"+order.getUserId();
        User user = restTemplate.getForObject(url,User.class);

        order.setUser(user);
        // 4.返回
        return order;
    }
}

 随后访问http://localhost:8080/order/101

可以看到,user信息可以查出来,远程调用成功

3.4 负载均衡

在上述远程调用基础上,我们通过在restTemplateConfig中添加注解@LoadBalance来实现负载均衡。

package cn.itcast.order.config;

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 RestTemplateConfig {

    @Bean
    @LoadBalanced
    public RestTemplate restTemplate(){
        return new RestTemplate();
    }

}

 orderservice:这时候访问路径就可以直接通过服务名称来查询

package cn.itcast.order.service;

import cn.itcast.order.mapper.OrderMapper;
import cn.itcast.order.pojo.Order;
import cn.itcast.order.pojo.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;

import javax.annotation.Resource;

@Service
public class OrderService {

    @Resource
    private OrderMapper orderMapper;

    @Autowired
    private RestTemplate restTemplate;

    public Order queryOrderById(Long orderId) {
        // 1.查询订单
        Order order = orderMapper.findById(orderId);

        //利用resttemplate发起http请求,查询用户信息,这个路径就是userservice的访问路径
//        String url = "http://localhost:8081/user/"+order.getUserId();
        String url = "http://userservice/user/"+order.getUserId();
        User user = restTemplate.getForObject(url,User.class);

        order.setUser(user);
        // 4.返回
        return order;
    }
}

 3.5 远程调用feign: 传统resttemplate还是有些麻烦复杂,如果有很多个服务需要远程调用,不可能做到每一个都输入url地址,会很麻烦,所以我们通过feign来实现远程调用替代restTemplate

首先创建一个新的module  feign,引入feign依赖

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

 创建Userclient远程调用接口,并且将user类写入到feign微服务中

package cn.itcast.feign.clients;

import cn.itcast.feign.pojo.User;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;

@FeignClient("userservice")     //服务名
public interface UserClient {

    @GetMapping("/user/{id}")
    User findById(@PathVariable("id") Long id);

}

然后在userservice和orderservice中引入feign依赖

<!--  openfeign远程调用      -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
        </dependency>

        <!--httpClient的依赖 -->
        <dependency>
            <groupId>io.github.openfeign</groupId>
            <artifactId>feign-httpclient</artifactId>
        </dependency>

<!--   引入feign     -->
        <dependency>
            <groupId>cn.itcast.demo</groupId>
            <artifactId>feign</artifactId>
            <version>1.0</version>
        </dependency>

然后在启动类上需要加上@EnableFeignClients(clients=UserClient.class)注解,clients=UserClient.class指定具体远程调用的是哪个服务

package cn.itcast.order;

import cn.itcast.feign.clients.UserClient;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.openfeign.EnableFeignClients;
import org.springframework.context.annotation.Bean;

@MapperScan("cn.itcast.order.mapper")
@SpringBootApplication
@EnableFeignClients(clients= UserClient.class)
public class OrderApplication {

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

}

修改orderservice,userclient替代restTemplate

package cn.itcast.order.service;

import cn.itcast.feign.clients.UserClient;
import cn.itcast.feign.pojo.User;
import cn.itcast.order.mapper.OrderMapper;
import cn.itcast.order.pojo.Order;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;

import javax.annotation.Resource;

@Service
public class OrderService {

    @Resource
    private OrderMapper orderMapper;

    @Autowired
    private RestTemplate restTemplate;

    @Autowired
    private UserClient userClient;

    public Order queryOrderById(Long orderId) {
        // 1.查询订单
        Order order = orderMapper.findById(orderId);

        //利用resttemplate发起http请求,查询用户信息,这个路径就是userservice的访问路径
//        String url = "http://localhost:8081/user/"+order.getUserId();
//        String url = "http://userservice/user/"+order.getUserId();
//        User user = restTemplate.getForObject(url,User.class);

        User user = userClient.findById(order.getUserId());

        order.setUser(user);
        // 4.返回
        return order;
    }
}

重新运行可以看到结果成功

 3.6 Gateway网关

添加相应依赖

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>cloud-demo</artifactId>
        <groupId>cn.itcast.demo</groupId>
        <version>1.0</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>gateway</artifactId>

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
    </properties>

    <dependencies>
        <!--网关-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-gateway</artifactId>
        </dependency>
        <!--nacos服务发现依赖-->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
        </dependency>
    </dependencies>

</project>

网关主要就是一些相应的配置:在application.yml配置如下信息 

server:
  port: 10010 # 网关端口
spring:
  application:
    name: gateway # 服务名称
  cloud:
    nacos:
      server-addr: localhost:8848 # nacos地址
    gateway:
      routes: # 网关路由配置
        - id: user-service # 路由id,自定义,只要唯一即可
          # uri: http://127.0.0.1:8081 # 路由的目标地址 http就是固定地址
          uri: lb://userservice # 路由的目标地址 lb就是负载均衡,后面跟服务名称
          predicates: # 路由断言,也就是判断请求是否符合路由规则的条件
            - Path=/user/** # 这个是按照路径匹配,只要以/user/开头就符合要求
#          filters:
#            - AddRequestHeader=sign, xn2001.com is eternal # 添加请求头
        - id: order-service
          uri: lb://orderservice
          predicates:
            - Path=/order/**
      globalcors: # 全局的跨域处理
        add-to-simple-url-handler-mapping: true # 解决options请求被拦截问题
        corsConfigurations:
          '[/**]':
            allowedOrigins: # 允许哪些网站的跨域请求 allowedOrigins: “*” 允许所有网站
              - "*"
            allowedMethods: # 允许的跨域ajax的请求方式
              - "GET"
              - "POST"
              - "DELETE"
              - "PUT"
              - "OPTIONS"
            allowedHeaders: "*" # 允许在请求中携带的头信息
            allowCredentials: true # 是否允许携带cookie
            maxAge: 360000 # 这次跨域检测的有效期

这里涉及到一些断言规则以及拦截规则,还有跨域请求问题。

还可以设置拦截功能,在访问的时候需要权限才可以访问,创建filter文件

 

package cn.itcast.gateway.filter;

import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.annotation.Order;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.stereotype.Component;
import org.springframework.util.MultiValueMap;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

@Component
@Order(-1)
public class AuthorizeFilter implements GlobalFilter {
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {

        //1.获取请求参数
        ServerHttpRequest request = exchange.getRequest();
        MultiValueMap<String, String> queryParams = request.getQueryParams();

        //2.获取参数中的authorization参数
        String auth = queryParams.getFirst("authorization");

        //判断参数值是否等于admin
        if("admin".equals(auth)){
            //是,放行
            return chain.filter(exchange);
        }

        //否,拦截
        exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);

        return exchange.getResponse().setComplete();
    }
}

最后启动运行:localhost:10010/order/101  出现如下页面,这是因为我们做了拦截,输入localhost:10010/order/101?authorization=admin才行

输入localhost:10010/order/101?authorization=admin

 再去查user信息也成功

 可以看到网关统一了访问端口都为10010,并且可以设置权限访问。

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

mozzm

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值