精华内容
下载资源
问答
  • SpringCloud feign整合FtpClient连接池实现文件上传下载微服务以及遇到的坑。 maven创建springboot 一 首先搭建zddts-ftp文件服务连接池及服务:zddts-ftp 服务提供者pom依赖的jar包,springcloud依赖的...

    感谢:Keepalived,和寄点以及网友分享的博客,以下是我整理的在

    相關連接:https://blog.csdn.net/qq_39914581/article/details/88660133

    https://blog.csdn.net/qq_17655941/article/details/80758133

    SpringCloud feign整合FtpClient连接池实现文件上传下载微服务以及遇到的坑。

    maven创建springboot

    一 首先搭建zddts-ftp文件服务连接池及服务:zddts-ftp 服务提供者pom依赖的jar包,springcloud依赖的全家桶此处不说明

    <!--ftp文件上传-->

    <dependency>

    <groupId>commons-net</groupId>

    <artifactId>commons-net</artifactId>

    <version>3.3</version>

    </dependency>

    <!--自定义连接池-->

    <dependency>

    <groupId>org.apache.commons</groupId>

    <artifactId>commons-pool2</artifactId>

    <version>2.4.2</version>

    </dependency>

    springboot启动类:

    package com.zddts;

    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    import org.springframework.boot.web.servlet.MultipartConfigFactory;
    import org.springframework.context.annotation.Bean;

    import javax.servlet.MultipartConfigElement;

    @SpringBootApplication
    public class ZddtsFtpApplication {

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

    /**
    * 文件上传配置
    *
    * @return
    */
    @Bean
    public MultipartConfigElement multipartConfigElement() {
    MultipartConfigFactory factory = new MultipartConfigFactory();
    // 单个数据大小
    factory.setMaxFileSize("20480KB"); // KB,MB
    /// 总上传数据大小
    factory.setMaxRequestSize("61440KB");
    return factory.createMultipartConfig();
    }
    }

    yml上配置ftp相关配置路径

     

    接下来写连接池:

    FtpClient工厂类:

     

    package com.zddts.ftp.core;
    
    import com.zddts.common.contants.ftp.FtpConstants;
    import org.apache.commons.net.ftp.FTP;
    import org.apache.commons.net.ftp.FTPClient;
    import org.apache.commons.net.ftp.FTPReply;
    import org.apache.commons.pool2.PooledObject;
    import org.apache.commons.pool2.PooledObjectFactory;
    import org.apache.commons.pool2.impl.DefaultPooledObject;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Component;
    
    import java.io.IOException;
    
    /**
     * 连接工厂
     */
    @Component
    public class FtpClientFactory implements PooledObjectFactory<FTPClient> {
        @Autowired
        FtpConfig ftpConfig;
    
    
        //创建连接到池中
        @Override
        public PooledObject<FTPClient> makeObject() {
            FTPClient ftpClient = new FTPClient();//创建客户端实例
            return new DefaultPooledObject<FTPClient>(ftpClient);
        }
    
        //销毁连接,当连接池空闲数量达到上限时,调用此方法销毁连接
        @Override
        public void destroyObject(PooledObject<FTPClient> pooledObject) {
            FTPClient ftpClient = pooledObject.getObject();
            try {
                ftpClient.logout();
                if (ftpClient.isConnected()) {
                    ftpClient.disconnect();
                }
            } catch (IOException e) {
                throw new RuntimeException("Could not disconnect from server.", e);
            }
        }
    
        //链接状态检查
        @Override
        public boolean validateObject(PooledObject<FTPClient> pooledObject) {
            FTPClient ftpClient = pooledObject.getObject();
            try {
                return ftpClient.sendNoOp();
            } catch (IOException e) {
                return false;
            }
        }
    
        //初始化连接
        @Override
        public void activateObject(PooledObject<FTPClient> pooledObject) throws Exception {
            FTPClient ftpClient = pooledObject.getObject();
            ftpClient.connect(ftpConfig.getIp(), ftpConfig.getPort());
            ftpClient.login(ftpConfig.getUserName(), ftpConfig.getPassWord());
            ftpClient.setFileType(FTP.BINARY_FILE_TYPE);//设置上传文件类型为二进制,否则将无法打开文件
            // 开启服务器对UTF-8的支持,如果服务器支持就用UTF-8编码,否则就使用本地编码(GBK).
            String LOCAL_CHARSET = "GBK";
            if (FTPReply.isPositiveCompletion(ftpClient.sendCommand("OPTS UTF8", "ON"))) {
                LOCAL_CHARSET = "UTF-8";
            }
            ftpClient.setControlEncoding(LOCAL_CHARSET);
        }
    
        //钝化连接,使链接变为可用状态
        @Override
        public void passivateObject(PooledObject<FTPClient> pooledObject) throws Exception {
            FTPClient ftpClient = pooledObject.getObject();
            try {
                ftpClient.changeWorkingDirectory(FtpConstants.root);
                ftpClient.logout();
                if (ftpClient.isConnected()) {
                    ftpClient.disconnect();
                }
            } catch (IOException e) {
                throw new RuntimeException("Could not disconnect from server.", e);
            }
        }
    
        // 用于连接池中获取pool属性
        public FtpConfig getConfig() {
            return ftpConfig;
        }
    }
    

    ftp yml配置实体类主要获取yml上ftp的相关配置(为了使用方便,我将部分配置分开定义在了常量类中FtpConstants ):

     

     

    常量类FtpConstants ,当然这部分配置也可以在yml中配置:

    package com.zddts.common.contants.ftp;
    
    /**
     * 说明:ftp配置类
     * Created by luojie on 2019/04/16.
     */
    public class FtpConstants {
    
    
        public static final String root = "/";
        public static int MaxTotal = 20;
        public static final int MinIdel = 1;
        public static final int MaxIdle = 8;
        public static final int MaxWaitMillis = 3000;
        /**
         * 本地字符编码
         */
        public static final String LOCAL_CHARSET = "UTF-8";
    
        // FTP协议里面,规定文件名编码为iso-8859-1
        public static final String SERVER_CHARSET = "iso-8859-1";
    
        /**
         * 各类文件存放规划路径目录名
         */
        public static final String WORKDIR_PAT = "/upload/pat";//专利文件主目录
        public static final String WORKDIR_PATDATAS = "datas"; // 专利资料存放路径
        public static final String WORKDIR_PATIMGS = "imgs"; // 专利图片存放路径
        public static final String WORKDIR_PATCONTRACT = "contract"; // 专利合同存放路径
        public static final String WORKDIR_PATPAY_ORDER_C = "payorderforc"; // 专利付款单 (对公司)
        public static final String WORKDIR_PATPAY_ORDER_P = "payorderforp"; // 专利付款单(对平台)
        public static final String WORKDIR_PAT_PROTOCOL_S = "protocols"; // 卖方专利转让协议文件
        public static final String WORKDIR_PAT_PROTOCOL_B = "protocolb"; //买方专利转让协议文件
    
    
        /**
         * 专利上传下载分类
         */
        public static final String TYPE_PATDATAS = "1"; // 专利资料上传下载
        public static final String TYPE_PATIMGS = "2"; // 专利图片上传下载
        public static final String TYPE_PATCONTRACT = "3"; // 专利合同上传下载
        public static final String TYPE_PATPAY_ORDER_C = "4"; // 专利付款单 (对公司)上传下载
        public static final String TYPE_PATPAY_ORDER_P = "5"; // 专利付款单(对平台)上传下载
        public static final String TYPE_PAT_PROTOCOL_S = "6"; // 卖方专利转让协议文件上传下载
        public static final String TYPE_PAT_PROTOCOL_B = "7"; // 买方专利转让协议文件上传下载
    
    
    /****
     ftp.Host=192.168.xx.xx    本机ip
     ftp.Port=21
     ftp.UserName=root
     ftp.PassWord=root
     ftp.workDir=/images
     ftp.encoding=utf-8
     ftp.root=/
     ftp.MaxTotal=100
     ftp.MinIdel=2
     ftp.MaxIdle=5
     ftp.MaxWaitMillis=3000
     **/
    }
    
    package com.zddts.ftp.core;
    
    import org.springframework.beans.factory.annotation.Value;
    import org.springframework.stereotype.Component;
    
    /**
     * 说明:ftp配置
     * Created by luojie on 2019/04/16.
     */
    
    @Component
    public class FtpConfig {
    
        @Value("${ftp.username}")
        private String userName;
    
        @Value("${ftp.password}")
        private String passWord;
    
        @Value("${ftp.host}")
        private String ip;
    
        @Value("${ftp.port}")
        private int port;
    
        public String getUserName() {
            return userName;
        }
    
        public void setUserName(String userName) {
            this.userName = userName;
        }
    
        public String getPassWord() {
            return passWord;
        }
    
        public void setPassWord(String passWord) {
            this.passWord = passWord;
        }
    
        public String getIp() {
            return ip;
        }
    
        public void setIp(String ip) {
            this.ip = ip;
        }
    
        public int getPort() {
            return port;
        }
    
        public void setPort(int port) {
            this.port = port;
        }
    }
    
    

    FptPool创建:

     

     

    package com.zddts.ftp.core;
    
    import com.zddts.common.contants.ftp.FtpConstants;
    import org.apache.commons.net.ftp.FTPClient;
    import org.apache.commons.pool2.impl.GenericObjectPool;
    import org.apache.commons.pool2.impl.GenericObjectPoolConfig;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Component;
    
    
    /**
     * fileName:ftpPool
     * description:FTP连接池
     * 1.可以获取池中空闲链接
     * 2.可以将链接归还到池中
     * 3.当池中空闲链接不足时,可以创建链接
     * author:luojie
     * createTime:2019-03-16 9:59
     */
    @Component
    public class FtpPool {
        FtpClientFactory factory;
        private final GenericObjectPool<FTPClient> internalPool;
    
        //初始化连接池
        public FtpPool(@Autowired FtpClientFactory factory) {
            this.factory = factory;
            GenericObjectPoolConfig poolConfig = new GenericObjectPoolConfig();
            poolConfig.setMaxTotal(FtpConstants.MaxTotal);
            poolConfig.setMinIdle(FtpConstants.MinIdel);
            poolConfig.setMaxIdle(FtpConstants.MaxIdle);
            poolConfig.setMaxWaitMillis(FtpConstants.MaxWaitMillis);
    
            this.internalPool = new GenericObjectPool<FTPClient>(factory, poolConfig);
        }
    
        //从连接池中取连接
        public FTPClient getFTPClient() {
            try {
                return internalPool.borrowObject();
            } catch (Exception e) {
                e.printStackTrace();
                return null;
            }
        }
    
        //将链接归还到连接池
        public void returnFTPClient(FTPClient ftpClient) {
            try {
                internalPool.returnObject(ftpClient);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    
        /**
         * 销毁池子
         */
        public void destroy() {
            try {
                internalPool.close();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
    
    
    

    FtpUtil 操作类:

     

     

    package com.zddts.ftp.core;
    
    import com.zddts.common.contants.ftp.FtpConstants;
    import com.zddts.common.utils.StringUtils;
    import org.apache.commons.net.ftp.FTPClient;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.core.io.ResourceLoader;
    import org.springframework.http.HttpHeaders;
    import org.springframework.http.HttpStatus;
    import org.springframework.http.ResponseEntity;
    import org.springframework.stereotype.Component;
    import org.springframework.web.multipart.MultipartFile;
    
    import javax.servlet.http.HttpServletRequest;
    import java.io.ByteArrayOutputStream;
    import java.io.IOException;
    import java.io.InputStream;
    import java.net.URLEncoder;
    
    import static com.zddts.common.contants.ftp.FtpConstants.LOCAL_CHARSET;
    
    /**
     * fileName:FTP工具类
     * description:提供文件上传和下载
     * author:luojie
     * createTime:2019-03-16 8:55
     */
    @Component
    public class FtpUtil {
    
        @Autowired
        private ResourceLoader resourceLoader;
        @Autowired
        FtpPool pool;
        @Autowired
        FtpConfig ftpConfig;
    
        /**
         * Description: 向FTP服务器上传文件
         *
         * @param file 上传到FTP服务器上的文件
         * @return 成功返回文件名,否则返回null
         * @Version2.0
         */
        public String[] upload(MultipartFile file, String workDirs) throws Exception {
            FTPClient ftpClient = pool.getFTPClient();
            //开始进行文件上传
            String fileName = file.getOriginalFilename();
            InputStream input = file.getInputStream();
            String[] ret = new String[2];
            if (input.available() == 0) {
                ret[0] = "0";
                ret[1] = "文件大小为0的文件请不要上传哦~~~";
                return ret;
            }
            try {
                ftpClient.setControlEncoding(FtpConstants.LOCAL_CHARSET);
    
                String remote = new String(workDirs + "/" + fileName);
    
    
                // 设置上传目录(没有则创建)
                if (!createDir(workDirs, ftpClient)) {
                    ret[0] = "0";
                    ret[1] = "切入FTP目录失败:" + workDirs;
                    return ret;
                }
    
    //            String remote = workDirs + "/" + fileName;
                //上传用被动模式
    //            ftpClient.enterRemotePassiveMode();
                ftpClient.enterLocalPassiveMode();  //设置被动模式
                boolean result = ftpClient.storeFile(remote, input);//执行文件传输
                if (!result) {//上传失败
                    ret[0] = "0";
                    ret[1] = "上传失败文件";
                    return ret;
                }
            } catch (Exception e) {
                e.printStackTrace();
                ret[0] = "0";
                ret[1] = "上传失败";
                return ret;
            } finally {//关闭资源
                input.close();
                System.out.println("开始归还连接");
                pool.returnFTPClient(ftpClient);//归还资源
            }
    
            ret[0] = "1";
            ret[1] = fileName;
            return ret;
    
        }
    
    
        /**
         * Description: 从FTP服务器下载文件
         *
         * @param pathName FTP服务器中的文件名
         * @param request  响应客户的响应体
         * @Version1.0
         */
        public ResponseEntity<byte[]> downLoad(String pathName, HttpServletRequest request) throws IOException {
            FTPClient ftpClient = pool.getFTPClient();
            String fileName = pathName.substring(pathName.lastIndexOf("/") + 1);
            HttpHeaders headers = new HttpHeaders();
            ResponseEntity<byte[]> entity = null;
            InputStream in = null;
            ByteArrayOutputStream byteOut = null;
            try {
    //            pathName = new String(pathName.getBytes(FtpConstants.LOCAL_CHARSET),
    //                    FtpConstants.SERVER_CHARSET);
                //下载用本地模式
                ftpClient.enterLocalPassiveMode();
                in = ftpClient.retrieveFileStream(pathName);
    
                //可以设置长度 在进行网络操作时往往出错,因为你调用available()方法时,
    //            对发发送的数据可能还没有到达,你得到的count是0。
    //            byte[] bytes = new byte[in.available()];
                int count = 0;
                while (count == 0) {
                    count = in.available();
                }
                byte[] bytes = new byte[count];
                byteOut = new ByteArrayOutputStream();
    //此处大于0 不然死循环
                int bufsize = 0;
                while ((bufsize = in.read(bytes, 0, bytes.length)) > 0) {
                    byteOut.write(bytes, 0, bufsize);
                }
                bytes = byteOut.toByteArray();
                //处理IE下载文件的中文名称乱码的问题
                String header = request.getHeader("User-Agent").toUpperCase();
                if (header.contains("MSIE") || header.contains("TRIDENT") || header.contains("EDGE")) {
                    fileName = URLEncoder.encode(fileName, LOCAL_CHARSET);
                    fileName = fileName.replace("+", "%20");    //IE下载文件名空格变+号问题
                } else {
                    //不转码 下载的文件名中文会变成 "_"
                    fileName = new String(fileName.getBytes(), FtpConstants.SERVER_CHARSET);
                }
    
                headers.add("Content-Disposition", "attachment;filename=" + fileName);
                headers.add("filename", fileName);
    //            headers.add("Access-Control-Expose-Headers", "Content-Disposition");
                headers.add("Access-Control-Expose-Headers", "filename");
                headers.setContentType(null);
                entity = new ResponseEntity<byte[]>(bytes, headers, HttpStatus.OK);
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
    //            注意要先关闭流 否则会出现卡死问题
                if (in != null) {
                    try {
                        in.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
                if (byteOut != null) {
                    try {
                        byteOut.close();
    
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
                System.out.println("开始归还连接");
                long bgn = System.currentTimeMillis();
                pool.returnFTPClient(ftpClient);
                long end = System.currentTimeMillis();
                System.out.println("归还连接耗时:" + (end - bgn));
    
            }
    
            return entity;
    
        }
    
        /**
         * Description: 从FTP服务器读取图片
         *
         * @param fileName 需要读取的文件名
         * @return 返回文件对应的Entity
         * @Version1.0
         */
    
        public ResponseEntity show(String fileName, String workDir) {
            String username = ftpConfig.getUserName();
            String password = ftpConfig.getPassWord();
            String host = ftpConfig.getIp();
            //ftp://root:root@192.168.xx.xx/+fileName
            return ResponseEntity.ok(resourceLoader.getResource("ftp://" + username + ":" + password + "@" + host + workDir + "/" + fileName));
        }
    
    
        /**
         * 创建目录(有则切换目录,没有则创建目录)
         *
         * @param dir
         * @return
         */
        public boolean createDir(String dir, FTPClient ftpClient) {
            if (StringUtils.nullOrBlank(dir)) {
                return true;
            }
            String d;
            try {
                //目录编码,解决中文路径问题
                d = new String(dir.toString().getBytes(LOCAL_CHARSET), "iso-8859-1");
                //尝试切入目录
                if (ftpClient.changeWorkingDirectory(d)) {
                    return true;
                }
                dir = StringUtils.trimStart(dir, "/");
                dir = StringUtils.trimEnd(dir, "/");
                String[] arr = dir.split("/");
                StringBuffer sbfDir = new StringBuffer();
                //循环生成子目录
                for (String s : arr) {
                    sbfDir.append("/");
                    sbfDir.append(s);
                    //目录编码,解决中文路径问题
                    d = new String(sbfDir.toString().getBytes("GBK"), "iso-8859-1");
                    //尝试切入目录
                    if (ftpClient.changeWorkingDirectory(d))
                        continue;
                    if (!ftpClient.makeDirectory(d)) {
                        System.out.println("[失败]ftp创建目录:" + sbfDir.toString());
                        return false;
                    }
                    System.out.println("[成功]创建ftp目录:" + sbfDir.toString());
                }
                //将目录切换至指定路径
                return ftpClient.changeWorkingDirectory(d);
            } catch (Exception e) {
                e.printStackTrace();
                return false;
            }
        }
    
    
    }
    

     

    以上整合ftp连接池完成,接下来就是Feign提供给其他服务接口FtpServerController :

     

     

    package com.zddts.ftp.server.controller;
    
    import com.zddts.common.utils.R;
    import com.zddts.ftp.core.FtpUtil;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.http.MediaType;
    import org.springframework.http.ResponseEntity;
    import org.springframework.web.bind.annotation.*;
    import org.springframework.web.multipart.MultipartFile;
    
    import javax.servlet.http.HttpServletRequest;
    
    /**
     * 说明:
     * Created by luojie on 2019/04/16.
     */
    @RestController
    @RequestMapping("/ftp/pturl")
    public class FtpServerController {
    
        //日志
        private Logger log = LoggerFactory.getLogger(FtpServerController.class);
        @Autowired
        FtpUtil ftpUtil;
    
        /**
         * 上传
         *
         * @param file
         * @param romte
         * @return
         * @throws Exception
         */
        @PostMapping(value = "/upload", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
        public R fileUpload(@RequestPart("file") MultipartFile file, @RequestParam("romte") String romte) throws Exception {
    
    
            //上传
            String filName = "";
    
    
            try {
                //上传
                filName = ftpUtil.upload(file, romte);
            } catch (Exception e) {
                log.error("上传失败详情:" + e.getMessage());
                return R.error("上传失败");
            }
            if (!"1".equals(filName)) {
                return R.error("上传失败" + filName);
            }
            return R.ok("上传成功");
    
        }
    
        /**
         * 下载
         *
         * @param romte
         * @param request
         * @throws Exception
         */
        @RequestMapping(value = "/download")
        public ResponseEntity<byte[]> download(String romte, HttpServletRequest request) throws Exception {
            ResponseEntity<byte[]> rep = null;
            try {
                //下载
                rep = ftpUtil.downLoad(romte, request);
            } catch (Exception e) {
                log.error("下载失败详情:" + e.getMessage());
            }
    
            return rep;
        }
    
    }
    

    至此服务提供方代码已经写好!

     

    接下来开始写服务消费方不:

     

     

     

    yml 配置:只要涉及到服务间文件传输的都需要配置 也可以配置在启动类中(默认好像是10mb)zuul网关的也需要设置:

     

    http:

        multipart:

             max-file-size: 20Mb # Max file size,默认1M

             max-request-size: 2500Mb # Max request size,默认10M

     

     

     

    addts-patmarket:pom中的依赖

    <!-- Feign进行跨服务传递文件依赖 -->

    <dependency>

    <groupId>io.github.openfeign.form</groupId>

    <artifactId>feign-form</artifactId>

    <version>3.0.3</version>

    </dependency>

    <dependency>

    <groupId>io.github.openfeign.form</groupId>

    <artifactId>feign-form-spring</artifactId>

    <version>3.0.3</version>

    </dependency>

    feign传输设置类告知对方传输文件类型:

    若不配置会报如下错误:

    Could not write request: no suitable HttpMessageConverter found for request type [org.springframework.web.multipart.support.StandardMultipartHttpServletRequest$StandardMultipartFile] and content type [multipart/form-data]

     

    package com.zddts.patmarket.client;

     

    import feign.codec.Encoder;

    import feign.form.spring.SpringFormEncoder;

    import org.springframework.beans.factory.ObjectFactory;

    import org.springframework.beans.factory.annotation.Autowired;

    import org.springframework.boot.autoconfigure.http.HttpMessageConverters;

    import org.springframework.boot.web.servlet.MultipartConfigFactory;

    import org.springframework.cloud.openfeign.support.SpringEncoder;

    import org.springframework.context.annotation.Bean;

    import org.springframework.context.annotation.Configuration;

    import org.springframework.context.annotation.Primary;

    import org.springframework.context.annotation.Scope;

     

    import javax.servlet.MultipartConfigElement;

     

    /**

    * 说明:文件上传下载 feign调用需要配置该文件

    * Created by luojie on 2019/04/16.

    */

    @Configuration

    public class MultipartSupportConfig {

     

     

    @Autowired

    private ObjectFactory<HttpMessageConverters> messageConverters;

     

     

    @Bean

    @Primary

    @Scope("prototype")

    public Encoder feignEncoder() {

    return new SpringFormEncoder(new SpringEncoder(messageConverters));

    }

     

    @Bean

    public feign.Logger.Level multipartLoggerLevel() {

    return feign.Logger.Level.FULL;

    }

     

    /**

    * 文件上传配置

    *

    * @return

    */

    @Bean

    public MultipartConfigElement multipartConfigElement() {

    MultipartConfigFactory factory = new MultipartConfigFactory();

    // 单个数据大小

    factory.setMaxFileSize("20MB"); // KB,MB

    /// 总上传数据大小

    factory.setMaxRequestSize("2500MB");

    return factory.createMultipartConfig();

    }

     

     

    }

     

    接口类FtpServerClient api

    package com.zddts.patmarket.client;

     

    import com.zddts.common.utils.R;

    import feign.codec.Encoder;

    import feign.form.spring.SpringFormEncoder;

    import org.springframework.beans.factory.ObjectFactory;

    import org.springframework.beans.factory.annotation.Autowired;

    import org.springframework.boot.autoconfigure.http.HttpMessageConverters;

    import org.springframework.cloud.openfeign.FeignClient;

    import org.springframework.cloud.openfeign.support.SpringEncoder;

    import org.springframework.context.annotation.Bean;

    import org.springframework.context.annotation.Configuration;

    import org.springframework.http.MediaType;

    import org.springframework.http.ResponseEntity;

    import org.springframework.web.bind.annotation.PostMapping;

    import org.springframework.web.bind.annotation.RequestParam;

    import org.springframework.web.bind.annotation.RequestPart;

    import org.springframework.web.multipart.MultipartFile;

     

    import javax.servlet.http.HttpServletRequest;

     

    /**

    * 说明:

    * Created by luojie on 2019/04/16.

    */

    @FeignClient("zddts-ftp")

    public interface FtpServerClient {

    @PostMapping(value = "/ftp/pturl/upload", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)

    public R fileUpload(@RequestPart(value = "file", required = true) MultipartFile file, @RequestParam("romte") String romte) throws Exception;

     

    @PostMapping(value = "/ftp/pturl/download")

    public ResponseEntity<byte[]> download(@RequestParam("romte") String romte) throws Exception;

     

    // /**

    // * 配置文件

    // */

    // @Configuration

    // class MultipartSupportConfig {

    // @Autowired

    // private ObjectFactory<HttpMessageConverters> messageConverters;

    //

    // @Bean

    // public Encoder feignFormEncoder() {

    // return new SpringFormEncoder(new SpringEncoder(messageConverters));

    // }

    //

    //

    // }

    }

     

    接下来就是真正调用的时候了:

    controller:

    package com.zddts.patmarket.filemage.controller;

     

    import com.zddts.common.utils.R;

    import com.zddts.patmarket.filemage.service.FileServerService;

    import org.springframework.beans.factory.annotation.Autowired;

    import org.springframework.http.ResponseEntity;

    import org.springframework.web.bind.annotation.*;

    import org.springframework.web.multipart.MultipartFile;

     

    import javax.servlet.http.HttpServletRequest;

    import javax.servlet.http.HttpServletResponse;

    import java.io.IOException;

     

    /**

    * 说明:文件上传功能

    * Created by luojie on 2019/04/16.

    */

    @RestController

    @RequestMapping("/file")

    public class FileServerController {

     

    @Autowired

    private FileServerService fileServerService;

     

    /**

    * 上传文件

    * 测试方法:

    *

    * @param file 待上传的文件

    * @return 文件在服务器上的绝对路径

    * @throws IOException IO异常

    */

    @PostMapping("/upload")

    public R fileUpload(@RequestPart("file") MultipartFile file, @RequestParam("type") String type) throws Exception {

     

     

    return fileServerService.fileUpload(file, type);

    }

     

     

    /**

    * 上传文件

    * 测试方法:

    *

    * @param patNo 待上传的文件

    * @return 文件在服务器上的绝对路径

    * @throws IOException IO异常

    */

    @RequestMapping(value = "/download")

    @ResponseBody

    public Object download(@RequestParam("patNo") String patNo, @RequestParam("consId") String consId, @RequestParam("orgNo") String orgNo, HttpServletRequest request, HttpServletResponse response) throws Exception {

     

    ResponseEntity entity = fileServerService.download(patNo, consId, orgNo, request);

    if (entity.getBody() == null) {

    return R.error("文件不存在");

    }

    return entity;

    }

     

    }

     

    service:

    package com.zddts.patmarket.filemage.service.impl;

     

    import com.zddts.common.contants.ftp.FtpConstants;

    import com.zddts.common.utils.R;

    import com.zddts.patmarket.client.FtpServerClient;

    import com.zddts.patmarket.filemage.service.FileServerService;

    import org.slf4j.Logger;

    import org.slf4j.LoggerFactory;

    import org.springframework.beans.factory.annotation.Autowired;

    import org.springframework.http.HttpStatus;

    import org.springframework.http.ResponseEntity;

    import org.springframework.stereotype.Service;

    import org.springframework.web.multipart.MultipartFile;

     

    import javax.servlet.http.HttpServletRequest;

     

    /**

    * 说明:文件上传服务

    * Created by luojie on 2019/04/16.

    */

    @Service

    public class FileServerServiceImpl implements FileServerService {

     

    //日志

    private Logger log = LoggerFactory.getLogger(FileServerServiceImpl.class);

    @Autowired

    FtpServerClient ftpServerClient;

     

    @Override

    public R fileUpload(MultipartFile file, String type) throws Exception {

     

    return ftpServerClient.fileUpload(file, FtpConstants.WORKDIR_PATDATAS);

    }

     

    @Override

    public ResponseEntity<byte[]> download(String patNo, String consId, String orgNo, HttpServletRequest request) throws Exception {

    //判断用户是否有权限

     

     

     

     

    String path = "/upload/pat/datas/papatentSort测试.csv";

    ResponseEntity<byte[]> entity = ftpServerClient.download(path);

     

     

    return entity;

    }

    }

     

    启动服务测试 :

     

    我用的是postman测试,测试完可以直接出接口文旦分享给团队成员很方便:

    上传:

    下载:

     

     

    注意:

    若ftp乱码上乱码,代码中已经处理了可能是工具有问题:

    xftp设置下使用utf-8编码

     

     

    以上就是springcloud 微服务ftp 基于连接池 上传的整合,需要考虑服务间超时问题,结合项目配置合理超时时间,我就不详细阐述了。

    感谢www,和分享的网友。

     

     

     

     

     

     

    展开全文
  • 微服务

    2017-03-09 10:35:40
    1.选择微服务部署策略 http://dockone.io/article/1066 2.资源下载 http://bbs.itheima.com/forum.php?mod=viewthread&amp;tid=338415 3.读取sql文件 http://www.jb51.net/article/103468.htm 4....

    1.选择微服务部署策略

    http://dockone.io/article/1066

     

    2.资源下载

    http://bbs.itheima.com/forum.php?mod=viewthread&tid=338415

     

    3.读取sql文件

    http://www.jb51.net/article/103468.htm

     

    4.spring boot war打包

    https://blog.csdn.net/pml18710973036/article/details/66968018

    tomcat去掉项目名访问

    https://www.cnblogs.com/airsen/p/6286269.html

     

    5.微服务spring cloud

    https://blog.csdn.net/cml_blog/article/details/78343323

    6.创建 springboot 项目

    https://www.jianshu.com/p/68c81aa54058

     

    展开全文
  • Dubbo的原理以及组件说明Dubbo支持哪些协议zookeeper注册中心介绍CAP原则Dubbo的注册中心zooker的调用原理zookeeper下载安装Zookeeper集群安装 什么是微服务微服务架构是一种架构风格和架构思想,它倡导我们在...

    什么是微服务?

    微服务架构是一种架构风格和架构思想,它倡导我们在传统软件应用架构的基础上,将系统业务按照功能拆分为更加细粒度的服务,所拆分的每一个服务都是一个独立的应用,这些应用对外提供公共的API,可以独立承担对外服务的职责,通过此种思想方式所开发的软件服务实体就是“微服务”,而围绕着微服务思想构建的一系列结,我们可以将它称之为“微服务架构
    微服务有什么样的具体特点呢?

    1、服务拆分粒度更细
    微服务可以说是更细维度的服务化,小到一个子模块,只要该模块依赖的资源与其他模块都没有关系,那么就可以拆分为一个微服务。

    2、服务独立部署
    传统的单体架构是以整个系统为单位进行部署,而微服务则是以每一个独立组件为单位进行部署。

    3、服务独立维护,分工明确
    每个微服务都可以交由一个小团队进行开发,测试维护部署,并对整个生命周期负责,当我们将每个微服务都隔离为独立的运行单元之后,任何一个或者多个微服务的失败都将只影响自己或者少量其他微服务,而不会大面积地波及整个服务运行体系。

    微服务它是由单体应用进化到服务化拆分部署,后期随着移动互联网规模的不断扩大,敏捷开发、DevOps 理论的发展和实践。随着微服务架构的越来越完善和发展,更多的企业已经使用这种架构方法。
    4.基于分布式 不是一个单独的框架
    5.他提倡将单一应用程序划分为一组小的服务 ——拆分
    6.每一个服务运行在其独立的进程中——独立的进程
    7.拥有自己独立的连接数据库——独立的数据库

    微服务架构的优缺点

    微服务架构是一种将单个应用程序作为一套小型服务开发的方法,每种应用程序都在自己的进程中运行,采用一组服务的方式来构建一个应用,服务独立部署在不同的进程中,不同服务通过一些轻量级交互机制来通信的架构思路。
    独立性
    在开发层面,每个微服务基本上都是各自独立的项目(project),而对应各自独立项目的研发团队基本上也是独立对应,这样的结构保证了微服务的并行研发,并且各自快速迭代,不会因为所有研发都投入一个近乎单点的项目,从而造成开发阶段的瓶颈。开发阶段的独立,保证了微服务的研发可以高效进行。
    可扩展性
    我们可以快速地添加服务集群的实例,提升整个微服务集群的服务能力,而在传统 Monolith 模式下,为了能够提升服务能力,很多时候必须强化和扩展单一结点的服务能力来达成。如果单结点服务能力已经扩展到了极限,再寻求扩展的话,就得从软件到硬件整体进行重构。
    隔离性
    隔离性实际上是可扩展性的基础,当我们将每个微服务都隔离为独立的运行单元之后,任何一个或者多个微服务的失败都将只影响自己或者少量其他微服务,而不会大面积地波及整个服务运行体系。
    在架构设计上有一种实践模式,即隔板模式(Bulkhead Pattern),这种架构设计模式的首要目的就是为了隔离系统中的各个功能单元和实体,使得系统不会因为一个单元或者服务的失败而导致整体失败。
    服务独立维护,分工明确
    每个微服务都可以交由一个小团队进行开发,测试维护部署,并对整个生命周期负责,当我们将每个微服务都隔离为独立的运行单元之后,任何一个或者多个微服务的失败都将只影响自己或者少量其他微服务,而不会大面积地波及整个服务。

    当然,没有完美无瑕的技术,微服务也有自身的不足:
    微服务应用是分布式系统,由此会带来固有的复杂性。开发者需要在RPC或者消息传递之间选择并完成进程间通讯机制。他们必须写代码来处理消息传递中速度过慢或者不可用等局部失效问题。当然这并不是什么难事,但相对于单体式应用中通过语言层级的方法或者进程调用,微服务下这种技术显得更复杂一些。

    Java中微服务架构与传统架构的区别

    在聊微服务之前,先来看看传统架构的优缺点。
    传统的 MVC 架构,所有的子系统都集成在一个很繁杂的 JVM 进程中。
    传统架构的优点:
    这种单体架构的优点在于方便管理,所有代码在同一项目中,但是当需求越来越多,项目规模越来越大,其坏处也很明显。
    传统架构的缺点:
    1、项目过于臃肿,部署效率低下
    当大大小小的功能模块都集中在同一项目的时候,整个项目必然会变得臃肿,让开发者难以维护。单体应用的代码越来越多,依赖的资源越来越多时,应用编译打包、部署测试一次非常耗时。系统高可用性差,资源无法隔离,整个单体系统的各个功能模块都依赖于同样的数据库、内存等资源,一旦某个功能模块对资源使用不当,整个系统都会被拖垮。
    2、开发成本高
    早期在团队开发人员只有两三个人的时候,协作修改代码,打包部署,更新发布这完全可以控制。但是团队一旦扩张,还是按照早期的方法去开发,那如果测试阶段只要有一块功能有问题,就得重新编译打包部署。所有的开发人员又都得参与其中,效率低下,开发成本极高。
    3、无法灵活拓展
    当系统的访问量越来越大的时候,单体系统固然可以进行水平扩展,部署在多台机器上组成集群:但是这种扩展并非灵活的扩展,因此,急需一种方法将应用的不同模块进行解耦,从而降低开发和部署成本。
    要解决上面单体应用的问题,就必须引入服务化的概念。

    Dubbo分布式服务框架

    Dubbo分布式服务框架

    SpringCloud

    1、为什么需要学习Spring Cloud

    不论是商业应用还是用户应用,在业务初期都很简单,我们通常会把它实现为单体结构的应用。但是,随着业务逐渐发展,产品思想会变得越来越复杂,单体结构的应用也会越来越复杂。这就会给应用带来如下的几个问题:
    代码结构混乱:业务复杂,导致代码量很大,管理会越来越困难。同时,这也会给业务的快速迭代带来巨大挑战;
    开发效率变低:开发人员同时开发一套代码,很难避免代码冲突。开发过程会伴随着不断解决冲突的过程,这会严重的影响开发效率;
    排查解决问题成本高:线上业务发现 bug,修复 bug 的过程可能很简单。但是,由于只有一套代码,需要重新编译、打包、上线,成本很高。

    由于单体结构的应用随着系统复杂度的增高,会暴露出各种各样的问题。近些年来,微服务架构逐渐取代了单体架构,且这种趋势将会越来越流行。Spring Cloud是目前最常用的微服务开发框架,已经在企业级开发中大量的应用。

    2、什么是Spring Cloud

    Spring Cloud是一系列框架的有序集合。它利用Spring Boot的开发便利性巧妙地简化了分布式系统基础设施的开发,如服务发现注册、配置中心、智能路由、消息总线、负载均衡、断路器、数据监控等,都可以用Spring Boot的开发风格做到一键启动和部署。Spring Cloud并没有重复制造轮子,它只是将各家公司开发的比较成熟、经得起实际考验的服务框架组合起来,通过Spring Boot风格进行再封装屏蔽掉了复杂的配置和实现原理,最终给开发者留出了一套简单易懂、易部署和易维护的分布式系统开发工具包。

    设计目标与优缺点

    设计目标
    协调各个微服务,简化分布式系统开发。

    优缺点
    微服务的框架那么多比如:dubbo、Kubernetes,为什么就要使用Spring Cloud的呢?

    优点:
    产出于Spring大家族,Spring在企业级开发框架中无人能敌,来头很大,可以保证后续的更新、完善
    组件丰富,功能齐全。Spring Cloud 为微服务架构提供了非常完整的支持。例如、配置管理、服务发现、断路器、微服务网关等;
    Spring Cloud 社区活跃度很高,教程很丰富,遇到问题很容易找到解决方案
    服务拆分粒度更细,耦合度比较低,有利于资源重复利用,有利于提高开发效率
    可以更精准的制定优化服务方案,提高系统的可维护性
    减轻团队的成本,可以并行开发,不用关注其他人怎么开发,先关注自己的开发
    微服务可以是跨平台的,可以用任何一种语言开发
    适于互联网时代,产品迭代周期更短

    缺点:
    微服务过多,治理成本高,不利于维护系统
    分布式系统开发的成本高(容错,分布式事务等)对团队挑战大

    总的来说优点大过于缺点,目前看来Spring Cloud是一套非常完善的分布式框架,目前很多企业开始用微服务、Spring Cloud的优势是显而易见的。因此对于想研究微服务架构的同学来说,学习Spring Cloud是一个不错的选择。

    Spring Cloud和SpringBoot版本对应关系

    Spring Cloud Version SpringBoot Version
    Hoxton 2.2.x
    Greenwich 2.1.x
    Finchley 2.0.x
    Edgware 1.5.x
    Dalston 1.5.x

    Spring Cloud和各子项目版本对应关系

    Component Edgware.SR6 Greenwich.SR2
    spring-cloud-bus 1.3.4.RELEASE 2.1.2.RELEASE
    spring-cloud-commons 1.3.6.RELEASE 2.1.2.RELEASE
    spring-cloud-config 1.4.7.RELEASE 2.1.3.RELEASE
    spring-cloud-netflix 1.4.7.RELEASE 2.1.2.RELEASE
    spring-cloud-security 1.2.4.RELEASE 2.1.3.RELEASE
    spring-cloud-consul 1.3.6.RELEASE 2.1.2.RELEASE
    spring-cloud-sleuth 1.3.6.RELEASE 2.1.1.RELEASE
    spring-cloud-stream Ditmars.SR5 Fishtown.SR3
    spring-cloud-zookeeper 1.2.3.RELEASE 2.1.2.RELEASE
    spring-boot 1.5.21.RELEASE 2.1.5.RELEASE
    spring-cloud-task 1.2.4.RELEASE 2.1.2.RELEASE
    spring-cloud-gateway 1.0.3.RELEASE 2.1.2.RELEASE
    spring-cloud-openfeign 暂无 2.1.2.RELEASE

    注意:Hoxton版本是基于SpringBoot 2.2.x版本构建的,不适用于1.5.x版本。随着2019年8月SpringBoot 1.5.x版本停止维护,Edgware版本也将停止维护。

    SpringBoot和SpringCloud的区别?

    SpringBoot专注于快速方便的开发单个个体微服务。
    SpringCloud是关注全局的微服务协调整理治理框架,它将SpringBoot开发的一个个单体微服务整合并管理起来,为各个微服务之间提供,配置管理、服务发现、断路器、路由、微代理、事件总线、全局锁、决策竞选、分布式会话等等集成服务

    SpringBoot可以离开SpringCloud独立使用开发项目, 但是SpringCloud离不开SpringBoot ,属于依赖的关系
    SpringBoot专注于快速、方便的开发单个微服务个体,SpringCloud关注全局的服务治理框架。

    使用 Spring Boot 开发分布式微服务时,面临以下问题

    (1)与分布式系统相关的复杂性 - 这种开销包括网络问题,延迟开销,带宽问题,安全问题。
    (2)服务发现 - 服务发现工具管理群集中的流程和服务如何查找和互相交谈。它涉及一个服务目录,在该目录中注册服务,然后能够查找并连接到该目录中的服务。
    (3)冗余 - 分布式系统中的冗余问题。
    (4)负载平衡 - 负载平衡改善跨多个计算资源的工作负荷,诸如计算机,计算机集群,网络链路,中央处理单元,或磁盘驱动器的分布。
    (5)性能问题 - 由于各种运营开销导致的性能问题。
    (6)部署复杂性 - DevOps的要求。

    服务注册和发现是什么意思?Spring Cloud 如何实现?

    当我们开始一个项目时,我们通常在属性文件中进行所有的配置。随着越来越多的服务开发和部署,添加和修改这些属性变得更加复杂。有些服务可能会下降,而某些位置可能会发生变化。手动更改属性可能会产生问题。Eureka 服务注册和发现可以在这种情况下提供帮助。由于所有服务都在 Eureka 服务器上注册并通过调用 Eureka 服务器完成查找,因此无需处理服务地点的任何更改和处理。

    负载平衡的意义什么?

    在计算中,负载平衡可以改善跨计算机,计算机集群,网络链接,中央处理单元或磁盘驱动器等多种计算资源的工作负载分布。负载平衡旨在优化资源使用,最大化吞吐量,最小化响应时间并避免任何单一资源的过载。使用多个组件进行负载平衡而不是单个组件可能会通过冗余来提高可靠性和可用性。负载平衡通常涉及专用软件或硬件,例如多层交换机或域名系统服务器进程。

    Eureka服务注册与发现

    • 服务治理
    • 服务注册
    • 服务发现Discovery——@EnableDiscoveryClient
    • actuator微服务信息完善
    • Eureka自我保护机制

    1. 什么是服务治理?
    Springcloud 封装了Netflix 公司开发的Eureka模块来实现服务治理。
    在传统的rpc 远程调用框架中,管理每个服务与服务之间依赖关系比较复杂,管理比较复杂,所以需要使用服务治理,管理服务与服务之间依赖关系,可以实现服务调用、负载均衡、容错等,实现服务发现与注册。

    2.什么是服务注册与发现?
    Eureka采用了CS的设计架构,Eureka Server 作为服务注册功能的服务器,它是服务注册中心。而系统中的其他微服务,使用Eureka的客户端连接到Eureka Server并维持心跳连接。这样系统的维护人员就可以通过Eureka Server来监控系统中各个微服务是否正常运行。

    在服务注册与发现中,有一个注册中心。当服务器启动的时候,会把当前自己服务器的信息 比如 服务地址通讯地址等以别名方式注册到注册中心上。另一方(消费者|服务提供者),以该别名的方式去注册中心上获取到实际的服务通讯地址,然后再实现本地RPC远程调用框架核心设计思想:在于注册中心,因为使用注册中心管理每个服务与服务之间的一个依赖关系(服务治理概念)。在任何rpc 远程框架中,都会有一个注册中心(存放服务地址相关信息(接口地址))
    在这里插入图片描述
    3.Eureka两组件

    • Eureka Server
    • Eureka Client

    Eureka Server提供服务注册服务
    各个微服务节点通过配置启动后,会在Eureka Server 中进行注册,这样Eureka Server中的服务注册表中将会存储所有可用服务节点的信息,服务节点的信息可以在界面中直观看到。

    Eureka Client通过注册中心进行访问
    是一个Java客户端,用于简化Eureka Server的交互,客户端同时也是一个内置的、使用轮询(round-robin)负载算法的负载均衡器。在应用启动后,将会向Eureka Server发送心跳(默认周期为30秒)。如果Eureka Server在多个心跳周期内没有收到某个节点的心跳,Eureka Server将会从服务注册表中把这个服务节点移除(默认90秒)。

    4、Eureka自我保护机制
    概述:
    保护模式主要用于一组客户端和Eureka Server之间存在网络分区场景下的保护,一旦进入保护模式,Eureka Server将会尝试保护其服务注册表中的信息,不再删除服务注册表中的数据,也就是不会注销任何微服务。
    一句话: 某时刻某一个微服务不可用了,Eureka不会立刻清理,依旧会对该微服务的信息进行保存。

    如果在Eureka Server的首页看到以下这段提示,则说明Eureka进入了保护模式。属于CAP里面的AP分支。
    在这里插入图片描述
    为什么会产生Eureka自我保护机制?
    为了防止EurekaClient可以正常运行,但是 与 EurekaServer网络不通情况下,EurekaServer不会立刻将EurekaClient服务剔除。

    什么是自我保护模式?
    默认情况下,如果EurekaServer在一定时间内没有接受到某个微服务实例的心跳,EurekaServer将会注销该实例(默认90秒)。但是当网络分区故障发生(延迟、卡顿、拥挤)时,微服务与EurekaServer之间无法正常通信,以上行为可能变得非常危险了——因为微服务本身其实是健康的,此时不应该注销这个微服务。Eureka通过“自我保护模式”来解决这个问题——当EurekaServer节点在短时间内丢失过多客户端时(可能发生了网络分区故障),那么这个节点就会进入自我保护模式。
    在这里插入图片描述
    在自我保护模式中,Eureka Server会保护服务注册表中的信息,不再注销任何服务实例。
    它的设计哲学就是宁可保留错误的服务注册信息,也不盲目注销任何可能健康的服务实例,

    综上: 自我保护模式是一种应对网络异常的安全保护措施。它的架构哲学是宁可同时保留所有微服务(健康的微服务和不健康的微服务都会保留)也不盲目注销任何健康的微服务。使用自我保护模式,可以让Eureka集群更加的健壮、稳定。

    Ribbon 负载均衡和 Rest 调用

    Ribbon:
    Ribbon 是一个软负载均衡的客户端组件,它可以和其他所需请求的客户端结合使用,和 eureka 结合只是其中的一个实例
    在这里插入图片描述
    1.负载均衡是什么
    用一句话说就是负载均衡+RestTemplate调用,其实就是一个软负载均衡的客户端组件。
    简单的说就是将用户的请求平摊的分布到多个服务上,从而达到系统的HA(高可用)。
    常见的负载均衡有软件Nginx,LVS,硬件F5等。

    2.Ribbon本地负载均衡客户端 VS nginx服务端负载均衡
    Nginx是服务器负载均衡,客户端所有请求都会交给Nginx,然后由nginx实现转发请求。即负载均衡是由服务端实现的。
    Ribbon本地负载均衡,在调用微服务接口时候,会在注册中心上获取注册信息服务列表之后缓存到JVM本地,从而在本地实现RPC远程服务调用技术。

    3.集中式LB
    即在服务的消费方和提供方之间使用独立的LB设施(可以是硬件,如F5,也可以是软件nginx)由该设施负责把请求通过某种策略转发至服务的提供方。

    4.进程内LB
    将LB逻辑集成到消费方,消费方从服务注册中心获取有哪些地址可用,然后自己在从这些地址中选择出一个合适的服务器。
    Ribbon就属于进程内LB,他是一个类库,集成于消费方进程,消费方通过他来获取服务提供方的地址。

    5.Ribbon在工作的时候分成两步
    第一步:先选择EurekaServer,它优先选择在同一个区域内负载较少的server
    第二步:再根据用户指定的策略,再从server取到的服务注册列表中选择一个地址
    其中Ribbon提供了多种策略:比如轮询、随机和根据响应时间加权重。

    6.引入ribbon依赖
    在这个版本中是不需要添加ribbon依赖的,因为最新版eureka依赖里面包含ribbon依赖的

    7.RestTemplate的使用
    getForObject()方法和/getForEntitiy()方法
    这两个的返回结果是一样的
    getForObject()返回对象为响应体中数据转化的对象,基本上可以理解为json。
    getForEntitiy()返回对象为ResponseEntity对象,包含了响应中的一些重要信息,比如响应头、响应状态码、响应体等。具体可以参照下图:
    在这里插入图片描述
    postForObject()方法和/postForEntity()方法同上
    总之一句话: Ribbon 就是 负载均衡 + RestTemplate调用,最终实现RPC的远程调用。

    Ribbon默认自带的负载规则 有7种
    ribbon中具体实现负载均衡的策略是通过IRule这个接口来实现的

    OpenFeign

    1.OpenFeign是什么?
    Feign是一个声明式的Web Service客户端。它的出现使开发Web Service客户端变得很简单。使用Feign只需要创建一个接口加上对应的注解,比如:FeignClient注解。Feign有可插拔的注解,包括Feign注解和JAX-RS注解。Feign也支持编码器和解码器,Spring Cloud Open Feign对Feign进行增强支持Spring MVC注解,可以像Spring Web一样使用HttpMessageConverters等。

    2、能干什么?(用在消费端)
    前面在使用Ribbon+RestTemplate时,利用RestTemplate对http请求的封装处理,形成了一套模板化的调用方法。但是在实际开发中,由于服务依赖的调用可能不止一处,往往一个接口会被多处调用,所以通常都会针对每一个微服务自行封装一些客户端类来包装这些依赖服务的调用。所以,Feign在此基础上面做了封装,由他来帮助我们定义和实现依赖服务接口的定义。在Feign的实现下,我们只需要建立一个接口并使用注解的方式来配置它(以前是dao接口上面标注Mapper注解,现在是一个微服务上面标注一个Feign注解即可),即完成对服务提供方的接口绑定,简化了使用spring cloud Ribbon时,自动封装服务调用客户端的开发量。

    3.Feign集成了Ribbon
    利用Ribbon维护了Payment的服务列表信息,并且通过轮询实现了客户端的负载均衡。而与Ribbon不同的是,通过Feign只要定义服务绑定接口且以声明式的方法,优雅而简单的实现了服务调用。

    6、OpenFeign超时控制
    默认Feign 客户端只等待1秒钟,但是服务端处理需要超过1秒钟,导致Feign 客户端不想等待了,直接返回报错。
    为了避免这样的情况,有时候我们需要设置Feign客户端的超时控制。

    7、OpenFeign超时,怎么解决?

    #80消费者yml配置文件中添加
    # 设置feign客户端超时时间(OpenFeign默认支持ribbon)
    ribbon:
      # 指的是建立连接所用的时间,适用于网络状态正常的情况下,两端连接所用的时间
      ReadTimeout: 5000
      # 指的是建立连接后从服务器读取到可用资源所用的时间
      ConnectTimeout: 5000
    

    8、OpenFeign的日志打印功能
    Feign提供了日志打印功能,
    我们可以通过配置来调整日志级别,从而了解Feign中Http请求的细节。说白了就是对Feign接口的调用情况进行监控和输出。
    Logger有四种类型:

    • NONE(默认):默认的,不显示任何日志
    • BASIC:仅记录请求方法、URL、响应状态码及执行时间
    • HEADERS:除了 BASIC 中定义的信息之外,还有请求和响应的头信息
    • FULL:除了 HEADERS 中定义的信息之外,还有请求和响应的正文及元数据
      在这里插入图片描述

    Hystrix——容错降级限流

    分布式系统面临的问题:
    复杂分布式体系结构中的应用程序有数十个依赖关系,每个依赖关系在某个时候将不可避免的失败。

    在这里插入图片描述
    服务雪崩:
    多个微服务之间调用的时候,假设微服务A调用微服务B和微服务C,微服务B和微服务C又调用其他的微服务,这就是所谓的 “ 扇出 ” 。
    如果扇出的链路上某个微服务的调用响应时间过长或者不可用,对微服务A 的调用就会占用越来越多的系统资源,进而引起系统崩溃,所谓的 “ 雪崩效应 ”。

    对于高流量的应用来说,单一的后端依赖可能会导致所有服务器上的所有资源都在几秒钟内饱和。比失败更糟糕的是,这些应用程序还可能导致服务之间的延迟增加,备份队列,线程和其他系统资源紧张,导致整个系统发生更多的级联故障。这些都表示需要对故障和延迟进行隔离和管理,以便单个依赖关系的失败,不能取消整个应用程序或系统。

    所以,通常当你发现一个模块下的某个实例失败后,这时候这个模块依然还会接受流量,然后这个有问题的模块还调用了其他的模块,这样就会发生级联故障,或者叫 雪崩。

    要避免这样的级联故障,就需要有一种链路中断的方案:
    服务降级、服务熔断

    Hystrix 简介:
    Hystrix是一个用于处理分布式系统的延迟和容错的开源库,在分布式系统里,许多依赖不可避免的会调用失败,比如超时、异常等,Hystrix能够保证在一个依赖出问题的情况下,不会导致整体服务失败,避免级联故障,已提高分布式系统的弹性。

    “ 断路器 ” 本身是一种开关装置,当某个服务单元发送故障之后,通过断路器的故障监控(类似熔断保险丝),向调用方返回一个符合预期的、可处理的备选响应(FallBack),而不是长时间的等待或抛出调用方无法处理的异常,这样就保证了服务调用方的线程不会被长时间、不必要地占用,从而避免了故障在分布式系统中的蔓延,乃至雪崩。

    Hystrix重要概念:

    • 服务降级 (fallback)
    • 服务熔断 (break)
    • 服务限流 (flowlimit)

    服务降级:
    服务器忙,请稍后再试,不让客户端等待并立刻返回一个友好提示,fallback
    哪些情况会发出降级?

    • 程序运行异常
    • 超时
    • 服务熔断触发服务降级
    • 线程池 / 信号量打满也会导致服务降级

    服务熔断:
    类似保险丝达到最大服务访问后,直接拒绝访问,拉闸限电,然后调用服务降级的方法并返回友好提示

    服务限流:
    秒杀高并发等操作,严禁一窝蜂的过来拥挤,大家排队,一秒钟N个,有序进行

    Spring Cloud 和dubbo区别?

    (1)服务调用方式:dubbo是RPC,Spring Cloud是Rest Api。
    (2)注册中心:dubbo 是zookeeper,Spring Cloud可以是zookeeper或其他。
    (3)服务网关:dubbo本身没有实现,只能通过其他第三方技术整合,springcloud有Zuul路由网关,作为路由服务器,进行消费者的请求分发,springcloud支持断路器,与git完美集成配置文件支持版本控制,事物总线实现配置文件的更新与服务自动装配等等一系列的微服务架构要素。
    (4)架构完整度:Spring Cloud包含诸多微服务组件要素,完整度上比Dubbo高。

    参考:https://mp.weixin.qq.com/s/iPUGbbWAns-x36qJD11txw

    展开全文
  • 在使用Feign做服务间调用的时候,当下载大的文件会出现堆栈溢出的情况。另外,文件管理服务(服务提供者)文件下载接口无返回值,是通过HttpServletRespoonse传输的流数据来响应,那么服务消费者该如何接受下载的数据呢...

    在使用Feign做服务间调用的时候,当下载大的文件会出现堆栈溢出的情况。另外,文件管理服务(服务提供者)文件下载接口无返回值,是通过HttpServletRespoonse传输的流数据来响应,那么服务消费者该如何接受下载的数据呢?

    一. 示例介绍

    8db78a376463fd6b0799095f455c836a.png

    我们调用feign_upload_second的下载文件接口下载文件,feign_upload_second内部使用feign调用feign_upload_first实现文件下载。

    二、feign_upload_first服务提供者

    服务提供者下载文件接口

    @RequestMapping(value = "/downloadFile",method = RequestMethod.GET,consumes = MediaType.APPLICATION_JSON_UTF8_VALUE)

    public void downloadFile(HttpServletResponse response){

    String filePath = "D://1.txt";

    File file = new File(filePath);

    InputStream in = null;

    if(file.exists()){

    try {

    OutputStream out = response.getOutputStream();

    in = new FileInputStream(file);

    byte buffer[] = new byte[1024];

    int length = 0;

    while ((length = in.read(buffer)) >= 0){

    out.write(buffer,0,length);

    }

    } catch (IOException e) {

    e.printStackTrace();

    } finally {

    if(in != null){

    try {

    in.close();

    } catch (IOException e) {

    e.printStackTrace();

    }

    }

    }

    }

    }

    三、feign_upload_second服务消费者

    服务提供者远程调用接口

    @RequestMapping(value = "/downloadFile",method = RequestMethod.GET,consumes = MediaType.APPLICATION_JSON_UTF8_VALUE)

    Response downloadFile();

    用feign.Response来接收

    服务提供者下载文件接口

    @RequestMapping(value = "/download",method = RequestMethod.GET)

    public ResponseEntity downFile(){

    ResponseEntity result=null ;

    InputStream inputStream = null;

    try {

    // feign文件下载

    Response response = uploadService.downloadFile();

    Response.Body body = response.body();

    inputStream = body.asInputStream();

    byte[] b = new byte[inputStream.available()];

    inputStream.read(b);

    HttpHeaders heads = new HttpHeaders();

    heads.add(HttpHeaders.CONTENT_DISPOSITION, "attachment;filename=123.txt");

    heads.add(HttpHeaders.CONTENT_TYPE,MediaType.APPLICATION_JSON_VALUE);

    result = new ResponseEntity (b,heads, HttpStatus.OK);

    } catch (IOException e) {

    e.printStackTrace();

    } finally {

    if(inputStream != null){

    try {

    inputStream.close();

    } catch (IOException e) {

    e.printStackTrace();

    }

    }

    }

    return result;

    }

    以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持脚本之家。

    展开全文
  • 下载微服务项目 首先在github上下载一下微服务包后,以mavan项目导入Eclipse。 https://github.com/microservices-demo/microservices-demo 微服务项目 此项目模拟的是一个商城管理系统,项目通过一个名为...
  • SpringBoot微服务搭建代码下载,里面包含SpringBoot服务端及一个客户端,是一个简单的实例。
  • 微服务架构如何落地实践 PDF 下载
  • 源码分享:Javaweb练手项目下载 微服务架构是一个庞大复杂的工程,为什么说它庞大复杂呢?因为想要做好微服务,就必须先要建设好微服务所需的一系列基础设施和组件。我在前面的文章《架构设计之「 微服务入门」》中...
  • 主要为大家详细介绍了使用Feign实现微服务间文件下载,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
  • 采用SpringCloud+SpringBoot+Netflix+mybatis敏捷框架,同时整合swagger在线文档、oralce/mysql数据库、redis缓存、Ribbon负载均衡、Hystrix熔断、minio文件容器、easypoi导入导出模板等常用组件,并且源码中还提供...
  • 微服务简介

    2018-10-09 03:09:17
    微服务简介 克里斯·理查森(ChrisRichardson)。 编辑-这个由七部分组成的系列文章现已完成: 微服务导论 构建微服务:使用API网关 构建微服务:微服务体系结构中...您还可以下载完整的文章集,以及有关使用N...
  • Nginx微服务搭建-下载器 1、pcre软件安装 准备目录:mkdir -p /data/{server, softs}/nginx 切换目录:cd /data/softs 下载:wget ...
  • 微服务通过openfeign获取文件流 问题描述: 微服务通过openfeign获取文件流,消费端获取的inputSteam=null,无法获取到文件流信息 解决方案: file服务(提供者):根据附件id,获取附件路径下载 @ApiOperation...
  • Jupiter是一套由斗鱼开源公布的微服务治理框架,它提供有丰富的后台功能,配置功能、应用资源管理功能等,可以将微服务框架标准化,统一注册、日志、监控等,将微服务的每个模板都进行可视化的管理与治理。...
  • SpringCloud与Docker微服务架构实战pdf文档与视频下载SpringCloud与Docker微服务架构实战pdf文档与视频下载
  • 微服务文件上传下载方案

    千次阅读 2018-11-14 19:47:04
      具体需求: 一 文件上传: ... 上传成功后需要服务端返回成功标识...(4) 上传下载的表本身与具体业务没有关系   (5) 前端先调上传服务,再调业务服务来完成整个业务   二 文件下载: (1) 前端给后端提供...
  • 微服务设计

    2019-04-11 09:09:15
    电子版仅供预览及学习交流使用,下载后请24小时内删除,支持正版,喜欢的请购买正版书籍 电子书下载(皮皮云盘-点击“普通下载”) 购买正版 封页 编辑推荐 过去十年中,分布式系统的粒度变得越来越细,包含大量...
  • 微服务资料.zip 微服务讲义.zip 通用资料.zip 十次方微服务day01.zip 十次方微服务day02.zip 十次方微服务day03.zip 十次方微服务day04.zip 十次方微服务day05.zip 十次方微服务day06.zip 十次方微服务day07.zip ...
  • Java微服务.pdf

    2020-09-08 11:25:15
    Java微服务+.txt 文件里有下载地址 Java微服务+.txt 文件里有下载地址 Java微服务+.txt 文件里有下载地址
  • 微服务实例demo

    2018-12-29 17:24:43
    这是一个微服务是实际操作的案例,一套完整的微服务demo,欢迎同学们下载学习。
  • 下载安装包 直接点击下载 启动 IDEA 配置参数 对各个服务,agent 文件都是一样的,但是服务名每个都需要单独指定哦 热力图、折线图
  • 微服务-源码

    2021-02-10 03:36:18
    微服务 创建此存储库是为了使用Java构建示例微服务。 贡献者包括: Akinola John(服务开发者) Favor Chukwuedo(前端API...要测试此服务,请下载Service Jars和Starters文件夹,并按照自述文件中的说明进行操作。
  • 1年前就听说Spring Cloud微服务,但是工作中也用不到,一直也没有深入了解、学习一下。...在网上找到了Spring Cloud微服务实战视频教程的百度网盘下载链接Spring Cloud微服务实战视频教程百度网盘下载 ...
  • 关键字:微服务 基础架构 基础技术栈 中间件 技能图谱 知识图谱 学习路线图 知识体系图 高清图下载:点我下载 特别说明:本资源收集于网络,版权归原作者及版权商所有,仅限个人学习研究之用,请勿做商业用途。如果...
  • Feign实现微服务间文件下载

    千次阅读 2018-09-15 09:31:14
    在使用Feign做服务间调用的时候,当下载大的文件会出现堆栈溢出的情况。另外,文件管理服务(服务提供者)文件下载接口无返回值,是通过HttpServletRespoonse传输的流数据来响应,那么服务消费者该如何接受下载的数据...
  • 本书基于Spring Cloud Edgware RELEASE 与Docker 17.09,以指导技术团队实现微服务架构落地为宗旨,覆盖微服务理论、微服务开发框架(Spring Cloud)及运行平台(Docker)三大主题。全书分为3部分,第1部分对微服务...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 2,444
精华内容 977
关键字:

下载微服务